0
点赞
收藏
分享

微信扫一扫

(P73+P74)stl(二+三):vector源码分析,内存分配子Allocator


文章目录

  • ​​1.vector源码分析​​
  • ​​2.vector动态数组内部如何实现连续空间​​
  • ​​3.内存分配子Allocator​​

1.vector源码分析

  • capacity表示能够容纳的元素的个数,capacity()>=size(),size()表示当前所容纳的元素的个数
    目的是:缓存一部分空间,下一次插入的时候,不需要分配内存,提供插入速度。
  • eg:P73\01.cpp

#include <vector>
#include <iostrem>
using namespace std;


int main(void)
{
//vector<int> 是一个模板类,定义一个对象v会引发模板类的构造函数的调用
vector<int> v;//在这里打个断点,跟踪

return 0;
}

vector模板类应该传递2个参数,_Ax是有默认参数的,下面的代码是vector模板的实现

(P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类


F11进去,这才是vector类模板的真正说明

(P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_02

template<class _Ty,
class _Ax = allocator<_Ty>>
class vector;

allocator本身是一个类模板,allocator<_Ty>是类模板的实例化,模板类_Ax
allocator就是一个内存分配子,stl会提供一个默认的内存分配子allocator<_Ty>,也可以实现为自己的内存方式(比如来自共享内存),
内存从哪里来,stl不关心

  • 接下来,构造vector,会调用基类的构造函数_MyBase()
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_03

  • 基类的类型使用typedef
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_04

  • 继续go进去,传递一个参数进来,传参之前先调用_Alloc()构造函数初始化给_Al,这里会调用_All的拷贝构造函数
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_05

  • _Alloc()构造函数,这里继续F11
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_06

  • _Al调用拷贝构造函数,这里继续F11
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_07

  • 要构造_Vector_val类模板,首先要构造基类_CONTAINER_BASE_AUX_ALLOC,然后再构造_Vector_val类模板对象成员,继续F11
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_08

  • 首先要构造基类_CONTAINER_BASE_AUX_ALLOC<_Alloc>的解释
    该基类又继承_Container_base基类,所以还要调用其基类的构造函数,继续go
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_09

  • 又是typedef,实际上_Container_base类就是_Container_base_secure,继续F11
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_10

  • 调用_Container_base_secure构造函数来构造基类对象,将0初始化给_myfirstiter指针
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_11

  • _Container_base_secure类的成员如下,但是是一个指针,就不会调用Iterator_base的构造函数
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_12

  • 此外由于_Container_base基类(也是_contoiner_base_aux_alloc_empty,用了typedef)没有数据成员,就直接构造函数体,_Alloc是类型参数
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_13

  • 接下来构造_Alval(_Al)对象成员,将内存分配子_Al对象初始化给_Alval(调用拷贝构造函数),但是担心_Alval和_Al类型不一样,所以使用了成员模板(在_Al中定义,在_Alval中使用),eg:_Ty传递是int类型,_Alloc传递是allocator<char>类型
    _Ty是int类型
    _Al的类型是allocator内存分配子模板类,即_Alloc;
    typename表示_Alloc是一个模板,template rebind<_Ty>::other是一个类型,是_Alloc中的一个成员模板,将typename _Alloc template rebind<_Ty>::other看成是一个类型,将整个类型重新定义成_Alty
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_14

  • 光标放到红框处的_Alloc,继续go下去
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_15

  • 再继续go
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_16

  • 对象成员构造完毕
  • 构造vector,基类的构造函数_MyBase()构造完毕,接下来会构造vector的数据成员,而vector内部只有3个指针,是不需要调用对象成员的构造函数(构造一个类,首先构造基类,再构造对象成员,最后是调用类构造函数本身)
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_17

  • 构造vector,基类的构造函数_MyBase()构造完毕,构造vector的数据成员完毕,接着构造函数体Buy(0)
    Buy(0)的意思是分配多少个空间
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_18


  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_19

  • vector内部的空间是连续的,但是又是可以动态增长的,这实际上是一个矛盾,解释如下:
    内部能够容纳的元素个数:capacity()
    vector内部所存放的元素个数:size()
    上述2个值不一定总是相等的,导致vector内部多了3个指针
    begin(),是闭区间,对应_Myfirst
    end(),是开区间,对应_Mylast
    只包含左边,不包含右边[ , )
    _Myend是整个容量的最后一个位置的下一个位置
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_20

  • 到目前为止初始化完毕
  • eg:P73\02.cpp
    仅仅跟踪push_back()的情况,而没有跟踪capacity()的情况
  • 当前的size()等于0,capacity()也等于0,push_back表示从尾部插入。
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_21


  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_22

    (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_23

  • 在end()处,继续F11,跟踪下去
    end()其实是依据_Mylast构造一个迭代器,也是一个模板类
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_24

  • 调用模板类的构造函数
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_25

  • 在insert(XXX)处F11,_Where表示当前插入的位置,返回的还是插入点的位置
    _Insert_n往当前的插入点插入一个元素_Val
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_26

  • _Insert_n(XXX)中继续F11,当前的容量capacity()等于0,_Count表示插入的元素个数,为1;
    max_size()表示能够容纳元素的最大个数
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_27

  • 调整过后,_Capacity=1,也就是要分配一个空间(也就是说第一次要分配一个空间)
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_28

  • max_size()中F11
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_29

  • 当前传递_Ty的大小是4个字节(int类型),-1是32bit整数的最大值,二进制表示就是32个1,转成成十进制就是40几个亿,所以_Count
    最多容纳10多亿个数据
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_30


  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_31

  • F10,直到返回
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_32

  • eg:P73\02.cpp

#include <vector>
#include <iostrem>
using namespace std;


int main(void)
{
//vector<int> 是一个模板类,定义一个对象v会引发模板类的构造函数的调用
vector<int> v;
v.push_back(1);//在这打个断点
cout<<v.capacity()<<endl;

v.push_back(1);
cout<<v.capacity()<<endl;

v.push_back(1);
cout<<v.capacity()<<endl;

v.push_back(1);
cout<<v.capacity()<<endl;


v.push_back(1);
cout<<v.capacity()<<endl;

v.push_back(1);
cout<<v.capacity()<<endl;

v.push_back(1);
cout<<v.capacity()<<endl;

return 0;
}

  • 测试
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_33

  • 大致是:增长的空间的算法是:增长为原来的一半;
    第一次插入是1,0+0/2=0,0<0+1,所以当前容量为1;
    第二次插入:由于是增长为原来的一半,1/2=0,0+1=1,所以只是增加1个空间,1<1+1,所以第二次插入时,当前的容量是1+1=2;
    第三次插入,由于是增长为原来的一半,2/2=1,2+1=3,所以当前的容量是3;
    以此类推
    注意,当前元素个数是5,但是容量是6的情况,说明它缓存了一个空间,缓存空间的目的是:下一次插入的时候,不需要分配内存,是为了减少频繁分配内存。到时候容器中元素个数越多,缓存的空间也会越多,因为是缓存一半的空间。
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_34

  • 总结:
    capacity():内部能够容纳的元素个数
    size():vector内部所存放的元素个数
    通常capacity() >= size(),原因是:向量缓存了一部分的内存空间,用来容纳更多的元素,这样,下一次插入新元素的时候,就不必重新分配内存,提供了插入速度;
    容量按照50%来扩充;
    _Myfirst对应第一个元素(对应begin()),_Mylast对应最后一个元素的下一个位置(对应end()),_Myend对应最后一个空间的下一个位置
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_35

  • 插入7个元素的空间分布如上:
    插入第二个元素,空间扩充为原来的一半,1+1/2=1,因为不足以容纳2个元素,所以需要分配2个空间出来;
    插入第6个元素的时候,缓冲了1个空间,下次插入的时候,就没必要分配空间了

2.vector动态数组内部如何实现连续空间

  • 向量尾部插入法:
    假设vector向量中有6个元素,其内存分布如下所示,要插入第7个元素,需要分配9个空间(此时这9个空间就是连续的),然后插入第7个元素,然后再把1-6给搬移下来
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_构造函数_36

  • 向量中间插入法
    比如在4的位置插入7,同样会先分配9个空间,然后先插入7
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_ci_37

  • 然后将1-3搬下来,将4-6搬到7后面
  • (P73+P74)stl(二+三):vector源码分析,内存分配子Allocator_模板类_38

3.内存分配子Allocator


举报

相关推荐

0 条评论