今天在看侯建的《STL源码剖析》时看到插入操作时发现多了一个拷贝构造的时候,有点疑惑,于是自己做了一个实验,对vector的push_back()操作时的内存分配进行了仔细的了解。
简单来说,push_back()就是往vector之后插入元素,这也是vector跟array最大的区别,array只能是固定大小,而有了vector之后,就可以动态扩大其容量了。
这里解释一下vector插入时的一些基本原理。
因为vector使用的alloc(用于分配内存)一直是从堆或者内存池(内存池应该是也属于堆)中获取内存,所以vector中所有的元素肯定不是位于栈上,就是我有一个int a=3,那么a肯定是位于栈上的,但是vector<int>b;b.push_back(a);之后,b[0]虽然值等于a,但是和a已经不是一个对象了,vector会在堆上给其重新分配内存。
vector的capacity属性可由vector<int>b;...;b.capacity();获得,代表了当前vector分配的大小;也就是当前vector中所能容纳的元素的个数。vector在插入时,如果:插入数量+已有数量<=vector.capacity()就直接插入,而插入数量+已有数量>vector.capacity()时就需要申请更多的内存了。那么究竟是如何申请内存呢?
我们会依次尝试向vector中插入元素。查看vector究竟是如何分配内存的。
构造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中所有的元素全部析构掉,并释放内存。