11.STL

news2024/11/25 22:54:15

STL阶段

禁止复制

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文本查询扩展作业解析

get_file函数的作用就是进行预处理操作,将文件中的每一行的内容放在shared_ptr<vector<string>> file里面进行存储;然后对每一个单词进行处理,将单词与行号放在map<string, shared_ptr<set<size_t>>> wm
查询某个单词sought的时候,会构建Query对象,在Query的构造函数中,会构建一个WordQuery对象,并且使用基类的指针shared_ptr<Query_base> q指向new出来的WordQuery对象。然后再调用Query的eval方法与rep方法的时候,都会走多态。在走WordQuery的eval方法的时候会走TextQuery的query方法,在TextQuery的query函数中将待查询的单词、行号、每一行的内容放在QueryResult中,交给QueryResul的数据成员,最后调用print函数将结果打出来。

查询两个单词同时出现的情况

Query andq = Query(sought1) & Query(sought2);
查询sought1的时候会构建Query对象,在Query的构造函数中,会构建一个WordQuery对象,并且使用基类的指针shared_ptr<Query_base> q指向new出来的WordQuery对象。
查询sought2的时候会构建Query对象,在Query的构造函数中,会构建一个WordQuery对象,并且使用基类的指针shared_ptr<Query_base> q指向new出来的WordQuery对象。
andq也是一个Query对象,在Query的构造函数中,会构建一个AndQuery对象,并且使用基类的指针shared_ptr<Query_base> q指向new出来的AndQuery对象

//const Query &lhs = Query(sought1);
//const Query &rhs = Query(sought2);
inline Query operator&(const Query &lhs, const Query &rhs)
{
    std::shared_ptr<Query_base>  tmp(new AndQuery(lhs, rhs));
    return tmp;
    //return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs));//隐式转换
    //shared_ptr<Query_base>  t(new AndQuery(lhs, rhs));
    //return t;  Query(t)
}
Query(std::shared_ptr<Query_base> t)
Point pt = 10;//10--->Point(10, 0)

Query(std::shared_ptr<Query_base> query)
: q(query)
{}

QueryResult
AndQuery::eval(const TextQuery& text) const
{ 
    QueryResult left = lhs.eval(text), right = rhs.eval(text);
	
    shared_ptr<set<line_no> > ret_lines(new set<line_no>);  

   //取交集
    set_intersection(left.begin(), left.end(), right.begin(), right.end(),
                   inserter(*ret_lines, ret_lines->begin()));
    return QueryResult(rep(), ret_lines, left.get_file());
}

~hello

1 2 3 4 ...10
1 3 5 10
2 4 6 7 8 9
    
0 1 2 3 4 5 6 7 8 9
0 2 4 9
1 3 5 6 7 8
QueryResult
NotQuery::eval(const TextQuery& text) const
{
    QueryResult result = query.eval(text);
	
    shared_ptr<set<line_no> > ret_lines(new set<line_no>);

	QueryResult::line_it beg = result.begin(), end = result.end();

	vector<string>::size_type sz = result.get_file()->size();
    for (size_t n = 0; n != sz; ++n) 
    {
		if (beg == end || *beg != n) 
			ret_lines->insert(n); 
		else if (beg != end) 
			++beg; 
	}
	return QueryResult(rep(), ret_lines, result.get_file());
}
                 n
0 1 2 3 4 5 6 7 8 9
    
    
    
        end
0 2 4 9
        beg

ret:1 3 5  6 7 8

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

标准模板库概述

STL的六大组件

1、容器:用来存放数据的,数据结构。

  • 序列式容器 vector、deque、list
  • 关联式容器 set、map、multiset、multimap
  • 无序关联式容器 unordered_set、unordered_multiset、unordered_map、unordered_multimap

2、迭代器:看成是一种指针,广义指针(具备指针的功能)。可以访问容器中的元素。

3、算法:操作容器中的元素。

4、适配器:起到适配的作用。

  • 容器适配器 stack、queue、priority_queue
  • 迭代器适配器
  • 函数适配器

5、函数对象(仿函数):定制化操作。

6、空间配置器:进行空间的申请与释放操作的。使用 + 原理 + 源码

序列式容器

deque:双端数组。多个片段组成,片段内连续,片段间不连续。

list:双向链表。

迭代器的形式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

初始化(掌握

三种序列式容器,初始化的方法基本一样:

  • 无参
  • count个value
  • 迭代器
  • 大括号
// 1.1、创建一个空对象
vector<int> number;

// 1.2、创建count个value
vector<int> number2(10, 3);
vector<int> number3(10);

// 1.3、迭代器范围
int arr[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
vector<int> number4(arr, arr + 10);//[,)左闭右开

//1.4、使用大括号
vector<int> number5 = {1, 2, 3, 5, 6, 8, 7, 4};

遍历(掌握

注意:list没有下标访问

三种序列式容器,遍历的方法基本一样。对于vector与deque而言,可以使用:

  • 下标
  • 迭代器
  • for与auto

但是对于list而言,不能使用下标,但是另外两个遍历方式是可以的。

// 2.1、下标
for(size_t idx = 0; idx != number5.size(); ++idx){
    cout << number5[idx] << "  ";
}

// 2.2、迭代器
// 2.2.1
vector<int>::iterator it;
for(it = number5.begin(); it != number5.end(); ++it){
    cout << *it << "  ";
}
// 2.2.2
vector<int>::iterator it2 = number5.begin();
for(; it2 != number5.end(); ++it2){
    cout << *it2 << "  ";
}

// 2.3、for与auto
for(auto &elem : number5){
    cout << elem << "  ";
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在尾部进行插入与删除

总结:三种序列式容器在尾部进行插入与删除是完全一样的。

  • push_back()
  • pop_back()
number.push_back(200); 	//没有返回值
number.pop_back(); 		//没有返回值

在头部进行插入与删除

注意:vector没有头插

Q: 为何vector不能在头部进行插入与删除?

A: vector是**一端开口**的,如果提供在头部进行插入与删除的操作,那么动第一个元素,会导致后面的N个元素都需要移动,时间复杂度是O(N)

number.push_front(222);	//没有返回值
number.pop_front();		//没有返回值

vector源码阅读(了解

类之间的继承图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

源码
class  vector
{
public:
  typedef _Tp value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
    
};

const map<int, string>  number = {{1, "hello"}};
number[1];

注意:对于vector而言,下标与at都可以随机访问,但是at比下标更加安全一些,at有范围检查。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

deque源码阅读(了解

类的继承图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

容器的insert操作

总结:三种序列式容器都有四种插入的方法

  • 找一个位置插入一个元素
  • 找一个位置插入count个元素
  • 找一个位置插入迭代器范围的元素
  • 插入到括号

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

number.insert(it, 11); // 1 在迭代器位置插入元素11
number.insert(it, 5, 22); // 2 迭代器位置插入5个22
number.insert(it, vec.begin(), vec.end()); // 3
number.insert(it, {666, 999, 777, 555}); // 4

vector的迭代器失效(重要,坑

对于vector的insert操作而言,不知道每次插入元素的个数是多少,当在进行插入的过程中,元素个数过多之后会进行**扩容,而迭代器还是指向的老的空间,然后再使用该迭代器的时候会发生问题,因为迭代器已经失效**了。

解决方案:每次在使用迭代器之前,**将迭代器重新置位**即可。

vector的erase操作(重要

删除满足条件的元素的时候,后面的元素会前移,此时再执行++it,会漏掉某些元素,所以**不应该在删除的时候移动it**。

满足条件就删除,但是不移动迭代器的位置;没有删除元素的时候,才移动迭代器的位置

for(auto it = number.begin(); it != number.end(); ){
	if(4 == *it){ // 删除vector中所有的元素4
		number.erase(it);
	}else{
		++it;
	}
}

容器的清空

vector有:

  • clear:清空元素,使 size = 0
  • size:存了多少个元素
  • capacity:容器的容量
  • shrink_to_fit:回收多余的空间,使 capacity = size

deque有:

  • clear:清空元素,使 size = 0,但它仍保有分配的内存
  • size:存了多少个元素
  • shrink_to_fit:回收多余的空间

list有:

  • clear:清空元素,使 size = 0
  • size:存了多少个元素

其他操作(三个容器都有)

  • swap函数:进行两个容器之间内容的交换。

  • resize函数:可以改变容器中元素的个数。

  • front函数:可以获取第一个元素。

  • back函数:可以获取最后一个元素。

  • emplace_back函数:这个函数的作用是在容器的末尾就地构造一个元素,而不是先构造一个临时对象然后将其移动或复制到容器中。emplace_back 函数通常比 push_back 函数更高效,因为它避免了额外的构造和析构操作。当你有一个需要插入的右值引用(如临时对象)时,push_back 可能会执行一次移动构造,而 emplace_back 直接在容器管理的内存空间中构造对象。

list的特殊操作

  • reverse函数:链表逆置

  • sort函数:链表排序。参数可以使用 std::less<int>() std::greater<int>()CompareList() (自定义一个类重载函数调用运算符)

  • unique函数:去除**连续的**重复元素,一般配合sort使用

  • merge函数:两个链表合并(注意使用链表之前需要使用sort进行排序)

  • splice函数:从一个 list 转移元素给另一个

    1. 移动所有元素

      number.splice(it, number2); //it为number的迭代器,操作完成后,number2变为空
      
    2. 移动一个元素

      number.splice(it, number2, it2); //将number2中的it2指向元素,移动到number的it的前面
      
    3. 移动迭代器范围的元素

      number.splice(it, number2, it2, it3); //将number2中的it2指向元素移动到number的it的前面
      

关联式容器

set的使用

基本特性

  • 存放的是key类型,key值是**唯一**的,不能重复
  • 默认会按照key值进行**升序**排列(从小到大)
  • 底层使用的是**红黑树**

查找操作

size_t cnt = number.count(8); 	//存在返回1,不存在返回0
auto it = number.find(7); 		//以迭代器的形式返回在容器中的位置。不存在迭代器就指向number.end()

set容器的insert操作

使用方法:

  • 找一个位置插入一个元素
  • 找一个位置插入迭代器范围的元素
  • 插入到括号
pair<set<int>::iterator, bool> ret = number.insert(7); //插入一个元素
number.insert(vec.begin(), vec.end()); //插入迭代器范围的元素
number.insert({6, 11, 7, 3, 21}); //插入大括号范围的元素

erase操作

number.erase(it); //删除迭代器指向的元素

set不支持修改,防止破坏红黑树的稳定性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不支持下标操作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

set针对于自定义类型如何进行排序(重要

  • 方法一:模板的特化(全特化) std::less根据特定模板Point进行的特化
  • 方法二:运算符重载,使用的是默认的std::less,但是两个Point不能比较大小,重载运算符来比较
  • 方法三:模板参数Compare使用自己的函数对象
#include <math.h>
#include <iostream>
#include <set>
#include <vector>
#include <utility>

using std::cout;
using std::endl;
using std::set;
using std::vector;
using std::pair;

template <typename Container>
void display(const Container &con)
{
    for(auto &elem : con){
        cout << elem << "  ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {}

    float getDistance() const { return hypot(_ix, _iy); }
    int getX() const { return _ix; }
    int getY() const { return _iy; }

    ~Point(){}

    friend std::ostream &operator<<(std::ostream &os, const Point &rhs);
    friend bool operator<(const Point &lhs, const Point &rhs);
    friend struct ComparePoint;
private:
    int _ix;
    int _iy;
};

std::ostream &operator<<(std::ostream &os, const Point &rhs)
{
    os << "(" << rhs._ix
       << ", " << rhs._iy
       << ")";

    return os;
}

// ========================= 运算符重载 =======================
// 方法二:运算符重载,使用的是默认的std::less,但是两个Point不能比较大小,重载运算符来比较
bool operator<(const Point &lhs, const Point &rhs)
{
    cout << "bool operator<" << endl;
    if(lhs.getDistance() < rhs.getDistance()) {
        return true;
    }
    else if(lhs.getDistance() == rhs.getDistance()) {
        if(lhs._ix < rhs._ix) {
            return true;
        }
        else if(lhs._ix == rhs._ix) {
            if(lhs._iy < rhs._iy) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

// ======================= 函数对象 ==========================
// 方法三:模板参数Compare使用自己的函数对象
struct ComparePoint
{
    bool operator()(const Point &lhs, const Point &rhs) const {
        cout << "struct ComparePoint" << endl;
        if(lhs.getDistance() < rhs.getDistance()) {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance()) {
            if(lhs._ix < rhs._ix) {
                return true;
            }
            else if(lhs._ix == rhs._ix) {
                if(lhs._iy < rhs._iy) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
};

//模板的特化
//命名空间的扩展
namespace std
{
// ========================= 模板的特化(全特化) ========================
// 方法一:模板的特化 std::less根据特定模板Point进行的特化
template <>
struct less<Point>
{
    bool operator()(const Point &lhs, const Point &rhs) const {
        cout << "template <>" << endl;
        if(lhs.getDistance() < rhs.getDistance()) {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance()) {
            if(lhs.getX() < rhs.getX()) {
                return true;
            }
            else if(lhs.getX() == rhs.getX()) {
                if(lhs.getY() < rhs.getY()) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
};

}
void test()
{
    set<Point> number = {
    /* set<Point, ComparePoint> number = { */
        Point(1, 2),
        Point(-1, 2),
        Point(1, 2),
        Point(3, 2),
        Point(1, -2),
        Point(4, -2),
    };
    display(number);
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

multiset的使用

基本特性

  • 存放的是key类型,**key值是不唯一**的,可以重复
  • 默认会按照key值进行升序排列(从小到大)
  • 底层使用的是红黑树

查找

  • count
  • find

bound函数

auto it = number.lower_bound(3); //返回第一个大于等于给定key值的迭代器

auto it2 = number.upper_bound(3); //返回第一个大于给定key值的迭代器

pair<multiset<int>::iterator, multiset<int>::iterator> ret = number.equal_range(3);
//返回的是一个范围,第一个迭代器指向的是大于等于给定key的迭代器,第二个迭代器是大于给定key的迭代器

插入操作

基本与set中的插入操作是一样,但是对于multiset而言,元素可以重复,所以插入肯定是可以成功的,返回类型不会像set一样,有pair类型。

erase操作

erase操作与set是一样的。也不支持修改、也不支持下标。

针对于自定义类型

自定义类型的三种写法:

  • 模板的特化
  • 函数对象的形式
  • 运算符重载

与set针对于自定义类型是完全一样的。

总结:

  • 元素都是有序的,默认都使用从小到大进行排序

  • 底层使用的都是红黑树数据结构

  • set中的元素是唯一的,但是multiset中元素是可以重复的

map的使用

基本特征

  • 存放的是key-value类型,**key值是唯一**的,不能重复;但是value值是可以重复的
  • 默认情况下,会**按照key值进行升序**排列
  • 底层实现也是使用的**红黑树**

查找操作

  • count
  • find

insert操作

pair<map<int, string>::iterator, bool> ret 
    // = number.insert({5, "dongjing"});
    // = number.insert(pair<int, string>(5, "dongjing"));
    = number.insert(make_pair(5, "dongjing"));

map的下标(重要

  • 查找,不存在就插入
  • 修改

multimap的使用

基本特征

  • 存放的是key-value类型,**key值是不唯一**的,可以重复;但是value值是可以重复的
  • 默认情况下,会按照key值进行升序排列
  • 底层实现使用的也是红黑树

其他基本操作

  • count
  • find
  • insert

不支持下标

下标传进来的是key类型,而**multimap的key是可以重复的**

总结:

  • key值都是**有序的,默认都使用从小到大**进行排序

  • 底层使用的都是**红黑树**数据结构

  • map中的元素key值是唯一的,但是multimap中key值是可以重复的

无序关联式容器

哈希

哈希函数

index = H(key)

通过哈希函数找到key值对应的位置。

构建哈希函数的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

哈希冲突

H(key1) = H(key2),  key1 != key2

不同的key值通过哈希函数运算之后,位置值是一样的。

解决哈希冲突的方法

开放定址法、链地址法 (推荐使用这种,这也是STL中使用的方法)、再散列法、建立公共溢出区

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

装载因子

装载因子 = 元素的个数/表长 [0.5,0.75].装载因子值越低,代表冲突的概率越低,内存的使用率越低;装载因子的值越大,代表冲突的概率越高,内存的使用率也越高。数组其实就是一个完美的哈希。

unordered_set的使用

模板参数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基本特征

  • 存放的是key类型,key值是**唯一**的,不能重复
  • 元素是**没有顺序**的
  • 底层使用的是**哈希**

其他操作

  • count
  • find
  • insert
  • erase

不支持修改、不支持下标访问

针对于自定义类型

针对于std::hash进行改造

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

std::equal_to的改造

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

unordered_multiset的使用

基本特征

  • 存放的是key类型,key值是**不唯一**的,可以重复
  • 元素是**没有顺序**的
  • 底层使用的是**哈希**

针对于自定义类型

与unordered_set针对于自定义类型是完全一样的。

unordered_map的使用

基本特征

  • 存放的是key-value类型,key值是**唯一的,不能重复;但是value值不唯一**,是可以重复的
  • key值是**无序**的
  • 底层实现使用的是**哈希**

其他的操作

  • count
  • find
  • insert
  • erase
  • 下标操作

unordered_multimap的使用

基本特征

  • 存放的是key-value类型,key值是**不唯一**的,可以重复;但是value值是可以重复的
  • key是值**没有顺序**的
  • 底层实现使用的**哈希**

其他操作

  • count
  • find
  • insert
  • erase

unordered_multimap不支持下标

容器的选择(重要

有没有顺序

首先选择的是,关联式容器,元素都是有序的。最不应该想到的是,无序关联式容器,元素是没有顺序的。
备选方案,可以选择序列式容器。list有成员函数sort,vector与deque在算法库中有sort函数。

下标操作

vector、deque、map、unordered_map是具备下标。

时间复杂度

序列式容器,时间复杂度O(N)。

关联式容器,时间复杂度O(logN)。

无序关联式容器,时间复杂度O(1)。

迭代器的类型

序列式容器vector与deque,是随机访问迭代器。

关联式容器以及list,是双向迭代器。

无序关联式容器,是前向迭代器。

是不是所有的容器都有迭代器

容器适配器stack、queue、priority_queue是没有迭代器的。

优先级队列

模板类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基本使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

针对于自定义类型

需要改写std::less,可以使用三种方式:模板的特化、函数对象的形式、运算符重载。

迭代器

基本概念

可以将迭代器看成是一种指针,但是不能完全等同于普通类型的指针,因为迭代器有可能是一个类类型,只是类类型中重载了指针一些运算符。

分类

随机访问迭代器、双向迭代器、前向迭代器、输入迭代器、输出迭代器

输出流迭代器

流对应有缓冲区,而缓冲区是可以用来存数据的,所以将流可以看成是容器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

class ostream_iterator
{
public:
    //ostream_iterator<int> osi(cout, "\n");
    //ostream_type& __s = cout;
    //const _CharT* __c = "\n"
    ostream_iterator(ostream_type& __s, const _CharT* __c) 
    : _M_stream(&__s)//_M_stream = &cout;
    , _M_string(__c) //_M_string = "\n"
    {
        
    }
    // ------------------------------------------------------------------------------
    ostream_iterator<_Tp>& operator=(const _Tp& __value) 
    { 
        *_M_stream << __value; // 等价 cout << 1
        if (_M_string) *_M_stream << _M_string; // 等价cout << "\n"
        return *this;
    }
	// -------------------------------------------------------------------------------
    ostream_iterator<_Tp>& operator*() 
    {
        return *this;
    }
    ostream_iterator<_Tp>& operator++() 
    { 
        return *this; 
    } 
   	ostream_iterator<_Tp>& operator++(int) 
    { 
        return *this; 
    } 
    // ----------------------------------------------------------------------------------
private:
    ostream_type* _M_stream;
    const _CharT* _M_string;
}; // end of class ostream_iterator

// ===========================================================================================

              last 
1, 3, 5, 7, 6
              f
// 参数:
// _InputIter __first = vec.bein();
// _InputIter __last = vec.end();
// _OutputIter __result = osi
inline _OutputIter __copy(_InputIter __first, _InputIter __last,
                          _OutputIter __result,input_iterator_tag, _Distance*)
{
    for ( ; __first != __last; ++__result, ++__first)
        *__result = *__first; // *__first解引用运算符重载得到1,调用拷贝构造函数
    return __result;
}
// ==============================================================================================
operator=(const int &rhs)
*osi = 1;

Point pt = 10;

输入流迭代器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

class istream_iterator
{
public:
    // 参数:
    // istream_iterator<int> isi(std::cin);
    // istream_type& __s = cin
    istream_iterator(istream_type& __s) 
    : _M_stream(&__s) //_M_stream = &cin
    { 
        _M_read(); 
    }
    // ----------------------------------------------------------------
    istream_iterator() 
    : _M_stream(0)
    , _M_ok(false) 
    {}
    // ----------------------------------------------------
    void _M_read() 
    {
        _M_ok = (_M_stream && *_M_stream) ? true : false;
        if (_M_ok) 
        {
            *_M_stream >> _M_value;//cin >> _M_value = 3
            _M_ok = *_M_stream ? true : false;
        }
    }
    // ----------------------------------------------------
    bool _M_equal(const istream_iterator& __y) const
    { 
        return (_M_ok == __y._M_ok) && (!_M_ok || _M_stream == __y._M_stream); 
    }
    // ----------------------------------------------------
    reference operator*() const 
    { 
        return _M_value; 
    }
    
    istream_iterator& operator++() 
    { 
        _M_read(); 
        return *this;
    }
  
    istream_iterator operator++(int) 
    {
        istream_iterator __tmp = *this;
        _M_read();
        return __tmp;
    }
// ----------------------------------------------------
private:
    istream_type* _M_stream;
    _Tp _M_value;
    bool _M_ok;
};

// 解析:copy(isi, istream_iterator<int>(), std::back_inserter(vec));
// 参数:
// _InputIter __first = isi;
// _InputIter __last = istream_iterator<int>();
// _OutputIter __result = std::back_inserter(vec);
inline _OutputIter __copy(_InputIter __first, _InputIter __last,
                          _OutputIter __result,
                          input_iterator_tag, _Distance*)
{
    for ( ; __first != __last; ++__result, ++__first)
        *__result = *__first;
    return __result;
}

就是 *result = 1;


// -------------------------------------------------------------------------
inline bool operator!=(const istream_iterator& __x, const istream_iterator& __y) 
{
    return !__x._M_equal(__y);
}

// --------------------------------------------------------------------------
class back_insert_iterator
{
public:
     back_insert_iterator<_Container>& operator=(const int& __value) 
     { 
         container->push_back(__value);
         return *this;
     }
    
    back_insert_iterator<_Container>& operator*() { return *this; }
    back_insert_iterator<_Container>& operator++() { return *this; }
    back_insert_iterator<_Container>& operator++(int) { return *this; }
};

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代器适配器

三组插入迭代器

函数模板back_inserter的返回结果是类模板back_insert_iterator类型,底层调用了push_back

函数模板front_inserter的返回结果是类模板front_insert_iterator类型,底层调用了push_front

函数模板inserter的返回结果是类模板insert_iterator类型,底层调用了insert

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

反向迭代器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

算法

头文件

#include <algorithm>

算法库中的算法都是非成员函数

分类

  • 非修改式的算法 for_each、count、find、search
  • 修改式的算法 copyremove_if、replace、swap
  • 排序算法 sort
  • 二分搜索 lower_bound、upper_bound
  • 集合操作 set_intersection、set_union、set_difference
  • 堆操作 make_heap、push_heap、pop_heap
  • 取最值 max、min
  • 数值操作 atoi
  • 未初始化的内存操作 uninitialized_copy

for_each算法

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f ); // 第三个参数:一元函数

一元函数:函数的参数是一个。

二元函数:函数的参数是两个。

一元谓词(断言):函数的参数是一个,并且返回类型是bool。

二元谓词(断言):函数的参数是两个,并且返回类型是bool。

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

using std::cout;
using std::endl;
using std::for_each;
using std::copy;
using std::vector;
using std::ostream_iterator;

void func(int &value) { // 加上引用就是可修改的
    ++value;
    cout << value << " - ";
}

void test() {
    vector<int> vec = {1, 3, 7, 9, 5, 8};
    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, "  "));
    cout << endl;
    /* ------------------------ 1 --------------------------------- */
    for_each(vec.begin(), vec.end(), func); // 只要first不等于last,就会把first进行解引用,解引用得到的值交给第三个参数
    /* -------------------2. lambda表达式---->匿名函数------------------ */
    // for_each(vec.begin(), vec.end(), 
    //         [](int &value){
    //             ++value;
    //             cout << value << "  ";
    //         });
    /* ---------------------------------------------------------------- */
    cout << endl;

    copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, "  "));
    cout << endl;
}

int main(int argc, char *argv[]) {
    test();
    return 0;
}

lambda表达式

基本使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个部分的名字

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

捕获列表用法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

void func()
{
    cout << "hello func" << endl;
}

void test()
{
    int a = 100;
    /*
        []捕获列表
        ()函数的参数列表
        {}函数的函数体
    */
    []()->void{
        cout << "hello" << endl;
        // cout << a << endl; // error 没有捕获
    }();

    // lambda表达式默认是const的,如果想修改可以使用mutable
    // 值捕获
    [a]()->void{
        // ++a; // error
        cout << "a = " << a << endl;
        cout << "hello" << endl;
    }();

    [a]()mutable->void{
        ++a;
        cout << "a = " << a << endl;
        cout << "hello" << endl;
    }();

    // 引用捕获
    [&a]()->void{
        ++a;
        cout << "a = " << a << endl;
        cout << "hello" << endl;
    }();
    cout << "aaaaaaa = " << a << endl;
}

void test2()
{
    [](const string &name){
        cout << "===test2===" << endl;
        cout << "name = " << name <<endl;
    }("wangdao");

    cout << endl << endl;

    auto f = [](const string &name){
        cout << "test2" << endl;
        cout << "name = " << name <<endl;
    };
    f("test22222"); // 注释了就不会执行匿名函数

    cout << endl << endl;

    typedef void (*pFunc)(const string &); // C风格
    // using pFunc = void(const string &); // C++风格
    pFunc pf = [](const string &name){
        cout << "test2" << endl;
        cout << "name = " << name <<endl;
    };
    pf("wangdao wuhan");
}

int main(int argc, char *argv[])
{
    test2();
    return 0;
}
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

int gNum = 100;

void test()
{
    int num = 10;
    int age = 20;
    string name("wangdao");

    //值传递
    [num, name](const string &value){
        cout << "num = " << num << endl;
        cout << "name = " << name << endl;
        cout << "value = " << value << endl;
        cout << "gNum = " << gNum << endl;
    }("hello");

    cout << endl << endl;

    [&num, name](const string &value){
        ++num;
        cout << "num = " << num << endl;
        cout << "name = " << name << endl;
        cout << "value = " << value << endl;
    }("hello");

    cout << endl << endl;

    [&num, &name](const string &value){
        ++num;
        name = "wuhan";
        cout << "num = " << num << endl;
        cout << "name = " << name << endl;
        cout << "value = " << value << endl;
    }("hello");

    cout << endl << endl;

    cout << "num = " << num << endl;
    cout << "name = " << name << endl;

    cout << endl << "[=, &]"<< endl;
    // num使用引用传递,其他变量使用<值传递>
    [=, &num](const string &value){
        ++num;
        // name = "nihao"; // error
        cout << "num = " << num << endl;
        cout << "name = " << name << endl;
        cout << "age = " << age << endl;
        cout << "value = " << value << endl;
    }("hello");

    cout << endl << "[&, num]"<< endl;
    // num使用值传递,其他变量使用引用传递
    [&, num](const string &value){
        name = "wangdao";
        age = 30;
        // num = 999; // error
        cout << "num = " << num << endl;
        cout << "name = " << name << endl;
        cout << "age = " << age << endl;
        cout << "value = " << value << endl;
    }("hello");
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

remove_if

template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );

Removes all elements for which predicate p returns true.
移除谓词p返回true的所有元素。
1, 3, 4, 6, 8, 5, 3, 2
走到6时第一次返回迭代器
//remove_if(vec.begin(), vec.end(), func)
         f             lst
1, 3, 4, 6, 8, 5, 3, 2
         i->i 先做一次前置++

         f             lst
1, 3, 4, 6, 8, 5, 3, 2
         i->i->i->i
    
         f             lst
1, 3, 4, 3, 8, 5, 3, 2
         i->i->i->i

         	f          lst
1, 3, 4, 3, 8, 5, 3, 2
         i->i->i->i->i
    
         	f          lst
1, 3, 4, 3, 2, 5, 3, 2
         i->i->i->i->i
    
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p){
    first = std::find_if(first, last, p); // 先看find_if源码
    if (first != last){
        for(ForwardIt i = first; ++i != last; ){
            if (!p(*i)){ 
                // 8 > 4 返回true !p(*i)=false
                // 5 > 4
                // 3 < 4 将3赋给first
                // 2 < 4 将2赋给first
                *first++ = std::move(*i);
            }               
        }           
    }     
    return first;
}

						lst
1, 3, 4, 6, 8, 5, 3, 2
		 f
constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p)
{
    for (; first != last; ++first) {
        if (p(*first)) {
            return first;
        }
    }
    return last;
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

using std::cout;
using std::endl;
using std::for_each;
using std::remove_if;
using std::copy;
using std::vector;
using std::ostream_iterator;

bool func(int value)
{
    return value > 4;
}

void test0() {
    vector<int> vec = {1, 3, 4, 6, 8, 5, 3, 2};
    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;

    remove_if(vec.begin(), vec.end(), func);

    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;
}

void test1()
{
    vector<int> vec = {1, 3, 4, 6, 8, 5, 3, 2};
    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;

    // remove_if不能将满足条件的元素一次删除,需要配合erase使用
    // auto it = remove_if(vec.begin(), vec.end(), bind1st(std::less<int>(), 4));

    auto it = remove_if(vec.begin(), vec.end(), bind2nd(std::greater<int>(), 4));
    // auto it = remove_if(vec.begin(), vec.end(), [](int value){ return value > 4; });
    // auto it = remove_if(vec.begin(), vec.end(), func);
    vec.erase(it, vec.end());

    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;
    /*
    1  3  4  6  8  5  3  2  
    1  3  4  3  2  5  3  2
    */
}

int main(int argc, char *argv[])
{
    test0();
    return 0;
}

vector扩容导致程序崩溃

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

bind1st、bind2nd

绑定二元函数的第一个、第二个参数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

using std::cout;
using std::endl;
using std::for_each;
using std::remove_if;
using std::copy;
using std::vector;
using std::ostream_iterator;

bool func(int value)
{
    return value > 4;
}

void test0() {
    vector<int> vec = {1, 3, 4, 6, 8, 5, 3, 2};
    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;

    remove_if(vec.begin(), vec.end(), func);

    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;

    /* 
    输出:
    1  3  4  6  8  5  3  2  
    1  3  4  3  2  5  3  2
                   i-----i 这一串多的元素,需要使用erase删除    
    */
}

void test1()
{
    vector<int> vec = {1, 3, 4, 6, 8, 5, 3, 2};
    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;

    /* remove_if不能将vector中满足条件的元素一次删除,需要配合erase使用,这么设计是为了保证通用性,其他容器也可使用 */
    // auto it = remove_if(vec.begin(), vec.end(), func);
    // auto it = remove_if(vec.begin(), vec.end(), [](int value){ return value > 4; }); // 使用lambda表达式也ok
    /* 传入一个固定了一个参数的二元谓词 */
    // auto it = remove_if(vec.begin(), vec.end(), bind1st(std::less<int>(), 4)); // 固定第一个参数bind1st,用less 保留小于等于4的
    // auto it = remove_if(vec.begin(), vec.end(), bind2nd(std::greater<int>(), 4)); // 固定第二个参数bind2nd,用greater 保留小于等于4的
    // auto it = remove_if(vec.begin(), vec.end(), bind1st(std::greater<int>(), 4)); // 固定第一个参数bind1st,用greater 保留大于等于4的
    auto it = remove_if(vec.begin(), vec.end(), bind2nd(std::less<int>(), 4)); // 固定第二个参数bind2nd,用less 保留大于等于4的
    // auto it = remove_if(vec.begin(), vec.end(), bind2nd(4, std::less<int>())); // error
    vec.erase(it, vec.end());

    for_each(vec.begin(), vec.end(), [](int &value){ cout << value << "  "; });
    cout << endl;
}

int main(int argc, char *argv[])
{
    test1();
    return 0;
}

bind的使用(重要

模板形式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

引用折叠(模板可以进行推导,可以传左值或者右值)

& && = &;
&& && = &&;
& & = &;
&& & = &;

类似的:完美转发 std::forward

普通函数遇到参数为&&只能传右值

函数指针

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

bind的基本概念

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

function的使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绑定的类型

可以绑定普通函数、成员函数、甚至还可以绑定数据成员。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果使用的是地址传递,开销小,

使用值传递(传对象),开销大;

但是使用传地址,那么对象是不能提前销毁的,

但是传对象,那么可以提前销毁。

bind与function的结合使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成员函数适配器mem_fn

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

函数对象(仿函数)

所有与小括号进行结合展示函数含义的对象。

  • 函数名
  • 函数指针
  • 重载了函数调用运算符的类创建的对象

空间配置器(重要,难

头文件与成员函数

#include <memory>

//申请原始的未初始化的空间
T* allocate( std::size_t n );

//释放空间
void deallocate( T* p, std::size_t n );

//在指定空间构建对象
void construct( pointer p, const_reference val );

//销毁对象
void destroy( pointer p );

特点

空间配置器会将**空间的申请对象的构建**严格分开。

因为在STL中,元素的个数一般是批量创建,如果此时还创建一个对象就申请对应的空间,可能空间的申请回非常的频繁,那么也有可能会频繁的回收,那么频繁申请空间与回收空间,会导致效率比较低,所以就一次申请一大段空间,然后在该空间上构建对象。

应用

自定义vector实现

原理图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//一级空间配置器(malloc)
# ifdef __USE_MALLOC
typedef malloc_alloc alloc;
typedef __malloc_alloc_template<0> malloc_alloc;
class __malloc_alloc_template 
{
public:
  static void* allocate(size_t __n)
  {
      void* __result = malloc(__n);
      if (nullptr == __result) 
        __result = _S_oom_malloc(__n);//oom = out of memory

      return __result;
  }
  
  static void deallocate(void* __p, size_t /* __n */)
  {
      free(__p);
  }

};

//二级空间配置器(默认的空间配置器)
#else
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
class __default_alloc_template 
{
	enum {_ALIGN = 8};
    enum {_MAX_BYTES = 128};
    enum {_NFREELISTS = 16}; 
	
	union _Obj 
	{
        union _Obj* _M_free_list_link;
        char _M_client_data[1];    /* The client sees this.        */
	};
public:
	static void* allocate(size_t __n)
    {
		void* __ret = 0;
		
		if (__n > 128) 
		{
			__ret = malloc(__n);//调用的是malloc
		}
		else 
		{
			//16个自由链表 + 内存池
			//1、对于小空间而言,避免频繁申请空间与释放空间,也可以减少内存碎片的问题
			//2、在进行申请空间的时候,会涉及到用户态与内核态之间的频繁切换
		}

    return __ret;
  };
  
    static void deallocate(void* __p, size_t __n)
    {
    if (__n > 128)
      malloc_alloc::deallocate(__p, __n);
    else 
	{
      _Obj**  __my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[3]
      _Obj* __q = (_Obj*)__p;

    
      __q -> _M_free_list_link = *__my_free_list;
      *__my_free_list = __q;
    
    }
  }

};

#endif


class allocator 
{
	typedef alloc _Alloc;
public:
    _Tp* allocate(size_type __n, const void* = 0) 
	{
		return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp))) : 0;
	}
	
	void deallocate(pointer __p, size_type __n)
    { 
		_Alloc::deallocate(__p, __n * sizeof(_Tp)); 
	}
	
	void construct(pointer __p, const _Tp& __val) 
	{ 
		new(__p) _Tp(__val); //定位new表达式
	}
	
	
    void destroy(pointer __p) 
	{ 
	   __p->~_Tp(); 
	}
};


typename __default_alloc_template::_Obj* __default_alloc_template::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _NFREELISTS
# else
    __default_alloc_template::_NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

static  size_t _S_freelist_index(size_t __bytes) 
{
	return ((32 + 8-1)/8 - 1); = 4 - 1 = 3
}

//向上取整,得到8的整数倍
static size_t _S_round_up(size_t __bytes) //__bytes = 32
{ 
    return ((32 + 8-1) & ~(8 - 1); 
	39 & ~7
	39 = 32 + 4 + 2 + 1 = 0010 0111
	7 = 0000 0111    1111 1000
	0010 0111
&	1111 1000
    0010 0000 = 32
}


40 = 32 + 8 = 0010 1000
	0010 1000
&	1111 1000
    0010 1000 = 40 
	
	0001 1111
&	1111 1000	
    0001 1000
	
32---->32    33---->40
31---->32    25---->32
[25,32]------32
3.x  4



char* __default_alloc_template::_S_start_free = nullptr;
char* __default_alloc_template::_S_end_free = nullptr;
size_t __default_alloc_template::_S_heap_size = 0;


//1、申请32字节,内存池与堆空间中有足够的空间
 static void* allocate(size_t __n)//__n = 32
 {
    void* __ret = 0;
    else 
	{
      _Obj** __my_free_list = _S_free_list + _S_freelist_index(__n);
    
      _Obj* __result = *__my_free_list;
      if (__result == nullptr)
        __ret = _S_refill(_S_round_up(__n));
      else {
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
  };

//_S_refill切割
void* __default_alloc_template::_S_refill(size_t __n)//__n = 32
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);//640
    _Obj** __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;
	
	__my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[3]
	
	__result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) 
	  {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return (__result);
}

//__size = 32
//__nobjs = 20
//_S_chunk_alloc真正的进行申请空间
char* __default_alloc_template::_S_chunk_alloc(size_t __size, int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs = 32 * 20 = 640;
    size_t __bytes_left = _S_end_free - _S_start_free = 0;
	
	else {
        size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4)
		                         = 2 * 640 = 1280 ;
		 _S_start_free = (char*)malloc(1280);//申请堆空间
		 
		_S_heap_size += __bytes_to_get = 1280;
        _S_end_free = _S_start_free + __bytes_to_get;
        return (_S_chunk_alloc(__size, __nobjs));//递归调用
	}
	
	char* __result;
    size_t __total_bytes = __size * __nobjs = 32 * 20 = 640;
    size_t __bytes_left = _S_end_free - _S_start_free = 1280;
	
	if (__bytes_left >= __total_bytes) 
	{
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    }
}

//2、申请64字节,内存池与堆空间中有足够的空间
static void* allocate(size_t __n)//__n = 64
  {
    void* __ret = 0;

    else {
      _Obj* * __my_free_list= _S_free_list + _S_freelist_index(__n);//_S_free_list[7]
	  
	  _Obj*  __result = *__my_free_list;
      if (__result == nullptr)
        __ret = _S_refill(_S_round_up(__n));
      else {
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
}

//__n = 64
void* __default_alloc_template::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;
	
	__my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[7]

  
      __result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return (__result);
}

//__size = 64
//__nobjs = 20
char* __default_alloc_template::_S_chunk_alloc(size_t __size, int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs = 64 * 20 = 1280;
    size_t __bytes_left = _S_end_free - _S_start_free = 640;
	
	else if (__bytes_left >= __size) 
	{
        __nobjs = (int)(__bytes_left/__size) = 640/64 = 10;
        __total_bytes = __size * __nobjs = 64 * 10 = 640;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    }
}

//3、申请96字节,内存池与堆空间中有足够的空间
//__n = 96
static void* allocate(size_t __n)
{
    void* __ret = 0;
	else {
      _Obj* * __my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[11]
	  _Obj*  __result = *__my_free_list;
      if (__result == nullptr)
        __ret = _S_refill(_S_round_up(__n));
      else {
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
	}
}

//__n = 96
void* __default_alloc_template::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;
	
	 __my_free_list = _S_free_list + _S_freelist_index(__n);

      __result = (_Obj*)__chunk;//1920
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return (__result);
	
	

}

//__size = 96
//__nobjs = 20
char* __default_alloc_template::_S_chunk_alloc(size_t __size, int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs = 96 * 20 = 1920;
    size_t __bytes_left = _S_end_free - _S_start_free = 0;
	
	else 
	{
        size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4)
		                         =  2 * 1920 + _S_round_up(1280 >> 4)
								 = 3920;
		_S_start_free = (char*)malloc(3920);
		
		_S_heap_size += __bytes_to_get = 1280 + 3920 = 5200;
        _S_end_free = _S_start_free + __bytes_to_get;
        return(_S_chunk_alloc(__size, __nobjs));//递归调用	
	}
	
	char* __result;
    size_t __total_bytes = __size * __nobjs = 96 * 20 = 1920;
    size_t __bytes_left = _S_end_free - _S_start_free = 3920;
	
	if (__bytes_left >= __total_bytes) {
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    }
}

//4、申请72字节,内存池与堆空间中没有连续的72字节
//__n = 72
static void* allocate(size_t __n)
{
    void* __ret = 0;
	else {
      _Obj* * __my_free_list = _S_free_list + _S_freelist_index(__n);//_S_free_list[8]
	  _Obj*  __result = *__my_free_list;
      if (__result == nullptr)
        __ret = _S_refill(_S_round_up(__n));
      else {
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
	}
}


//__n = 72
void* __default_alloc_template::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;
	
	if (1 == __nobjs) return(__chunk);
}


//__size = 72
//__nobjs = 20
char* __default_alloc_template::_S_chunk_alloc(size_t __size, int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs = 72 * 20 = 1440;
    size_t __bytes_left = _S_end_free - _S_start_free = 0;
	
	
	else {
        size_t __bytes_to_get =  2 * __total_bytes + _S_round_up(_S_heap_size >> 4)
		                         > 2880 ;
		_S_start_free = (char*)malloc(__bytes_to_get);
		
		if (0 == _S_start_free) 
		{
            size_t __i;
            _Obj* * __my_free_list;
	        _Obj* __p;
			
			//72 80 88 96
			 for (__i = 72; __i <= 128;__i += 8) 
			 {
				 //_S_free_list[8] _S_free_list[9] _S_free_list[10] _S_free_list[11]
				 __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
				
				if (nullptr != __p) 
				{
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));//递归调用
                    
                }
			 }
		}
	}
	
	char* __result;
    size_t __total_bytes = __size * __nobjs = 72 * 20 = 1440;
    size_t __bytes_left = _S_end_free - _S_start_free = 96;
	
	else if (__bytes_left >= __size) 
	{
        __nobjs = (int)(__bytes_left/__size)  = 96/72 = 1;
        __total_bytes = __size * __nobjs = 72 * 1 = 72;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } 
}

总结:

allocate就是空间配置器进行申请空间的函数。
1、_S_freelist_index 取下标
2、_S_round_up 向上取整,得到8的整数倍
3、_S_refill 将申请回来的空间进行切割(按照指定大小切割多份,以链表的形式进行挂接)
4、_S_chunk_alloc 才是真正申请空间的函数,该函数有可能会调用malloc,该函数有可能会递归调用

空间配置器申请的空间都在堆上

https://blog.csdn.net/xy913741894/article/details/66974004

当自由链表中没有对应的内存块,系统会执行以下策略:
如果用户需要是一块n字节的区块,且n <= 128(调用第二级配置器),此时Refill填充是这样的:(需要注意的是:系统会自动将n字节扩展到8的倍数也就是RoundUP(n),再将RoundUP(n)传给Refill)。用户需要n块,且自由链表中没有,因此系统会向内存池申请nobjs * n大小的内存块,默认nobjs=20

  • 如果内存池大于 nobjs * n,那么直接从内存池中取出

  • 如果内存池小于nobjs * n,但是比一块大小n要大,那么此时将内存最大可分配的块数给自由链表,并且更新nobjs为最大分配块数x (x < nobjs)

  • 如果内存池连一个区块的大小n都无法提供,那么首先先将内存池残余的零头给挂在自由链表上,然后向系统heap申请空间,申请成功则返回,申请失败则到自己的自由链表中看看还有没有可用区块返回,如果连自由链表都没了最后会调用一级配置器

最后

也就是STL可能存在的问题,通俗的讲就是优缺点吧

我们知道,引入相对的复杂的空间配置器,主要源自两点:

  1. 频繁使用malloc,free开辟释放小块内存带来的性能效率的低下
  2. 内存碎片问题,导致不连续内存不可用的浪费

引入两层配置器帮我们解决以上的问题,但是也带来一些问题:

  1. 内碎片的问题,自由链表所挂区块都是8的整数倍,因此当我们需要非8倍数的区块,往往会导致浪费,比如我只要1字节的大小,但是自由链表最低分配8块,也就是浪费了7字节,我以为这也就是通常的以空间换时间的做法,这一点在计算机科学中很常见。

  2. 我们发现似乎没有释放自由链表所挂区块的函数?确实是的,由于配置器的所有方法,成员都是静态的,那么他们就是存放在静态区。释放时机就是程序结束,这样子会导致自由链表一直占用内存,自己进程可以用,其他进程却用不了。
    t __i;
    _Obj* * __my_free_list;
    _Obj* __p;

     	//72 80 88 96
     	 for (__i = 72; __i <= 128;__i += 8) 
     	 {
     		 //_S_free_list[8] _S_free_list[9] _S_free_list[10] _S_free_list[11]
     		 __my_free_list = _S_free_list + _S_freelist_index(__i);
             __p = *__my_free_list;
     		
     		if (nullptr != __p) 
     		{
                 *__my_free_list = __p -> _M_free_list_link;
                 _S_start_free = (char*)__p;
                 _S_end_free = _S_start_free + __i;
                 return(_S_chunk_alloc(__size, __nobjs));//递归调用
                 
             }
     	 }
     }
    

    }

    char* __result;
    size_t __total_bytes = __size * __nobjs = 72 * 20 = 1440;
    size_t __bytes_left = _S_end_free - _S_start_free = 96;

    else if (__bytes_left >= __size)
    {
    __nobjs = (int)(__bytes_left/__size) = 96/72 = 1;
    __total_bytes = __size * __nobjs = 72 * 1 = 72;
    __result = _S_start_free;
    _S_start_free += __total_bytes;
    return(__result);
    }
    }




## 总结:

allocate就是空间配置器进行**申请空间**的函数。
1、`_S_freelist_index` 取下标
2、`_S_round_up` 向上取整,得到8的整数倍
3、`_S_refill` 将申请回来的空间进行切割(按照指定大小切割多份,以链表的形式进行挂接)
4、`_S_chunk_alloc` 才是真正申请空间的函数,该函数有可能会调用malloc,该函数有可能会递归调用

空间配置器申请的空间都在堆上



https://blog.csdn.net/xy913741894/article/details/66974004



当自由链表中没有对应的内存块,系统会执行以下策略:
如果用户需要是一块n字节的区块,且n <= 128(调用第二级配置器),此时Refill填充是这样的:(需要注意的是:系统会自动将n字节扩展到8的倍数也就是RoundUP(n),再将RoundUP(n)传给Refill)。用户需要n块,且自由链表中没有,因此系统会向内存池申请nobjs * n大小的内存块,默认nobjs=20

- 如果内存池大于 nobjs * n,那么直接从内存池中取出

- 如果内存池小于nobjs * n,但是比一块大小n要大,那么此时将内存最大可分配的块数给自由链表,并且更新nobjs为最大分配块数x (x < nobjs)

- 如果内存池连一个区块的大小n都无法提供,那么首先先将内存池残余的零头给挂在自由链表上,然后向系统heap申请空间,申请成功则返回,申请失败则到自己的自由链表中看看还有没有可用区块返回,如果连自由链表都没了最后会调用一级配置器



## 最后

也就是STL可能存在的问题,通俗的讲就是优缺点吧

我们知道,引入相对的复杂的空间配置器,主要源自两点:

1. 频繁使用malloc,free开辟释放小块内存带来的性能效率的低下
2. 内存碎片问题,导致不连续内存不可用的浪费

引入两层配置器帮我们解决以上的问题,但是也带来一些问题:

1. 内碎片的问题,自由链表所挂区块都是8的整数倍,因此当我们需要非8倍数的区块,往往会导致浪费,比如我只要1字节的大小,但是自由链表最低分配8块,也就是浪费了7字节,我以为这也就是通常的以空间换时间的做法,这一点在计算机科学中很常见。
2. 我们发现似乎没有释放自由链表所挂区块的函数?确实是的,由于配置器的所有方法,成员都是静态的,那么他们就是存放在静态区。释放时机就是程序结束,这样子会导致自由链表一直占用内存,自己进程可以用,其他进程却用不了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C/C++】C++类与对象基本概念(抽象封装、类的定义与使用、构造函数、析构函数、静态成员、友元)

目录 七、类与对象基本概念7.1 抽象7.2 类的定义与声明7.3 访问控制7.4 类的实现与使用7.5 对象指针、this指针与对象引用7.6 构造函数7.7 析构函数7.8 拷贝构造函数7.9 类类型作为函数参数7.10 对象数组7.11 静态成员7.12 常对象与常成员&#xff08;const&#xff09;7.13 友…

金融知识普及月答题活动

金融知识普及月答题活动 关键词&#xff1a;金融安全、风险防范、金融常识、反诈宣传 推荐功能&#xff1a;答题、倡议书 宣传角度&#xff1a; 1. 普及金融知识&#xff1a;讲解货币、信用、利率、汇率等基本金融概念&#xff0c;以及储蓄、贷款、信用卡、保险等常见金融产…

揭秘Xinstall:如何降低你的App推广成本?

在移动互联网时代&#xff0c;App推广成为了每个开发者都必须面对的问题。然而&#xff0c;随着市场竞争的加剧&#xff0c;App推广成本也水涨船高&#xff0c;让许多开发者望而却步。今天&#xff0c;我们就来聊聊如何借助Xinstall这一神器&#xff0c;有效降低App推广成本&am…

电商人必看:1个工具,5倍效率,批量处理图片就是这么简单

作为电商运营者或经常处理图片的你&#xff0c;是否厌倦了繁琐的图片编辑工作&#xff1f;今天&#xff0c;我要分享一个实用的解决方案——图片批量处理工具。 神器介绍&#x1f447; 千鹿设计助手&#xff0c;是一款轻量级、功能非常丰富的设计插件工具合集软件。 拥有多款…

宏集MIRO-L230工业路由器: 一站式全球联网解决方案

在日益互联的世界中&#xff0c;全球覆盖的稳定连接已成为业务成功的关键因素。宏集非常高兴地向您介绍我们的最新创新产品——MIRO-L230工业路由器&#xff0c;这是一款为现代企业量身定制的LTE路由器&#xff0c;为用户带来前所未有的稳定连接体验。 宏集MIRO-L230工业路由器…

Leetcode3234. 统计 1 显著的字符串的数量

Every day a Leetcode 题目来源&#xff1a;3234. 统计 1 显著的字符串的数量 解法1&#xff1a;枚举左端点 注意到&#xff0c;如果子串中的 0 非常多&#xff0c;多到 0 的个数的平方比 1 的个数都要大&#xff0c;那么这样的子串必然不是 1 显著子串。 设 cnt0 为子串中…

使用excel把json文件转为表格

json文件格式 [ { "ID": "16", "名称": "测站", "管理ID": "3", "管理名称": "土", "辅助信息": { "百度经度&qu…

macos MacPort 包管理工具安装和使用

在macos v10.15版本中, xz, python等软件无法使用brew安装, 原因是brew对于旧版本的macos不再支持, 但是我们可以使用另外一个macos下的包管理工具来安装brew无法安装的软件, macport 是一个和brew类似的macos下的一个非常优秀的软件包安装管理工具. MacPort安装前提条件 安…

解锁高效API测试之道:Apipost安装

在当今这个以API为中心的数字时代&#xff0c;无论是开发、测试还是管理API&#xff0c;一个得心应手的工具都能显著提升工作效率。如果你正寻求一款既强大又易于使用的API测试工具&#xff0c;那么Apipost绝对是你不容错过的选择。 接下来&#xff0c;让我们一起去完成这款软件…

zsh 添加 用户@主机 路径显示

export PROMPT"%F{green}%n%F{blue}%F{yellow}%m%F{cyan}:[%~]%f %# " export PROMPT"%F{green}%n%F{blue}%F{yellow}%m%F{cyan}:[%~]%f %$ " bash则为 PS1\u\h:\w\$

vue3之vite配置vite-plugin-mock使用mock轻松创建模拟数据提高开发效率

文章目录 什么是Mock数据使用Mock数据的优点Mock数据使用步骤一、安装依赖mockjs、vite-plugin-mock二、vite.config.ts 文件中配置三、在根目录下创建mock文件四、编写api接口调用文件1、src文件夹下新建utils/request.ts2、src文件夹下新建api/user.ts 五、业务页面调用六、M…

ESXI8虚拟机vmdk文件备份到本地硬盘

1. Esxi开启ssh服务 点击“管理”→“服务” →“TSM-SSH”&#xff0c;右击启动 2. 把datastore1中所有虚拟机文件(包括vmdk文件)复制到d:\E1 使用xshell等工具SSH登录ESXI8后台&#xff0c;确认datastore1的具体文件夹路径 在win10&#xff0c;使用cmd&#xff0c;在d:\E1…

docker续3:dockerfile

一、使用Dockerfile创建应用镜像 在Docker file中定义所需要执⾏的指令&#xff0c;使⽤ docker build创建镜像&#xff0c;过程中会按照dockerfile所定义的内容进⾏打开临时性容器&#xff0c;把docker file中命令全部执⾏完成&#xff0c;就得到了⼀个容器应⽤镜像&#xff…

力扣第二阶段Days34

1,题目描述-找出字符串中所有的字母异位词 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 2&#xff…

安科瑞预付费电能计量装置 功能介绍

安科瑞DTSY1352 三相无线预付费电能表是为测量额定频率 50Hz 的三相交流有功电能设计的高性能设备。这些电能表不仅支持预付费控制、负载和时间控制&#xff0c;恶性负载控制等多种控制模块&#xff0c;还具备多种通信功能&#xff0c;包括 RS485、NB、4G、LoRa、LoRaWAN、Wi-F…

SpringIoc体系结构设计

IOC容器的整体功能 从这个图仔细体会Spring框架加载Bean的过程. 有一个全局观. 1:加载Bean的配置&#xff08;比如xml配置 注解配置)不同类型资源的加载&#xff0c;解析成生成统一Bean的定义. 2:根据Bean的定义加载生成Bean的实例&#xff0c;并放置在Bean容器中 3:对容器中…

【C++ Qt day2】

2.完成课堂上的多文件编译 head.h main.cpp student.cpp 3. 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h…

Sentinel-1 Level 1数据处理的详细算法定义(八)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程&#xff0c;以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下&…

PixPin 付费版 - 长截图贴图标注软件工具

在日常的工作和生活中&#xff0c;我们常常需要用到截图工具。而今天&#xff0c;给大家介绍一款功能超级强大的智能截图贴图工具 PixPin。 PixPin 适用于 Win 和 Mac 系统&#xff0c;支持自定义范围或窗口截图&#xff0c;让你可以精准地截取所需内容。 软件的长截图功能是它…

乱弹篇(43)我爱看短视频

今天&#xff0c;本“人民体验官”推广人民日报官方薇搏文化产品《你爱看短视频还是长视频》。 截图&#xff1a;来源“人民体验官”推广平台 人民微博告诉大家&#xff0c;今&#xff08;29日&#xff09;天发布的《中国互联网络发展状况统计报告》显示&#xff0c;截至2024年…