C++vector插入时的内存分配

    xiaoxiao2021-03-25  81

    C++vector插入时的内存分配

    今天在看侯建的《STL源码剖析》时看到插入操作时发现多了一个拷贝构造的时候,有点疑惑,于是自己做了一个实验,对vector的push_back()操作时的内存分配进行了仔细的了解。


    vector的push_back()操作原理

    简单来说,push_back()就是往vector之后插入元素,这也是vector跟array最大的区别,array只能是固定大小,而有了vector之后,就可以动态扩大其容量了。

    这里解释一下vector插入时的一些基本原理。

    vector中元素与待插入元素绝对不是同一个对象

    因为vector使用的alloc(用于分配内存)一直是从堆或者内存池(内存池应该是也属于堆)中获取内存,所以vector中所有的元素肯定不是位于栈上,就是我有一个int a=3,那么a肯定是位于栈上的,但是vector<int>b;b.push_back(a);之后,b[0]虽然值等于a,但是和a已经不是一个对象了,vector会在堆上给其重新分配内存。

    vector的capacity属性

    vector的capacity属性可由vector<int>b;...;b.capacity();获得,代表了当前vector分配的大小;也就是当前vector中所能容纳的元素的个数。vector在插入时,如果:插入数量+已有数量<=vector.capacity()就直接插入,而插入数量+已有数量>vector.capacity()时就需要申请更多的内存了。那么究竟是如何申请内存呢?

    实验

    我们会依次尝试向vector中插入元素。查看vector究竟是如何分配内存的。

    测试代码块

    #include<iostream> #include<vector> using namespace std; class A{ private: static int a; //标识A的数量 public: A(){ a++; cout<<a<<" "<<this<<endl;//显示新构造的A的对象的地址 } A(const A &b){ a++; cout<<a<<" "<<this<<endl;//显示由拷贝构造函数构造的A的对象的地址 } ~A(){ cout<<this<<" has been destoried!\n";//显示被析构掉的对象的地址 } }; int A::a=0; int main(){ vector<A>re; A s1; re.push_back(s1); cout<<"A capacity is :"<<re.capacity()<<" "<<&re[0]<<endl;//输出re分配的大小和首元素的地址 cout<<endl; A s2; re.push_back(s2); cout<<"A capacity is :"<<re.capacity()<<" "<<&re[0]<<endl; cout<<endl; A s3; re.push_back(s3); cout<<"A capacity is :"<<re.capacity()<<" "<<&re[0]<<endl; cout<<endl; A s4; re.push_back(s4); cout<<"A capacity is :"<<re.capacity()<<" "<<&re[0]<<endl; cout<<endl; A s5; re.push_back(s5); cout<<"A capacity is :"<<re.capacity()<<" "<<&re[0]<<endl; cout<<endl; return 0; }

    运行结果

    实验分析

    构造S1调用了普通构造函数,但是把他放入vector是调用的却是拷贝构造函数,而且拷贝构造函数构造出来的对象就是vector[0]; 构造S2是也是调用了普通构造函数,但却调用两次拷贝构造函数,和依次析构函数,显然可以看到析构掉的是之前的vector[0],而两个拷贝构造其中一个就是vector[0],那么另一个显然是vector1,就是新插入的元素。可以看出,当插入数量+已有数量>vector.capacity()时,系统会现将原有的vector中所有元素拷贝一份到新的地址中,然后将之前的vector中的元素全部析构掉。其实从《STL源码分析》中可以看到,此时vector中的start,finish和end_of_storage三个指针也同步更新了。 以此类推,我在最后依次插入S5的时候,因为5>re.capacity()=4,所以程序调用拷贝构造函数重新构造了之前的四个对象,当然刚开始还是构造了要push_back()进去的S5。最后再将之前的vector中的元素4个元素全部析构掉。

    结论

    vector在插入元素的时候,首先调用拷贝构造函数在堆中申请内存构造对象,如果插入对象数量没有超过vector的capacity,就直接插入;若超过了就在调用拷贝构造函数将之前vector中所有的元素都重新构造一边放入新的内存中,然后将之前vector中所有的元素全部析构掉,并释放内存。


    转载请注明原文地址: https://ju.6miu.com/read-16984.html

    最新回复(0)