观察vector.push_back()具体拷贝机制

实验需要条件

  1. vector容器
  2. 创建一个能通过拷贝控制函数打印相关信息的类,这里我自己写了一个简易的String类来打印信息

实验代码:

	vector<String> vec;

	String s = "1";
	cout << "--------第一个放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第二个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第三个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第四个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第五个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第六个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第七个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第八个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第九个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------第十个元素放入容器-------" << endl;
	vec.push_back(s);
	cout << "--------放入完毕-------" << endl;

实验结果:

请添加图片描述

分析实验结果

看到实验结果一开始是头皮发麻的,其实通过查找规律,可以验证侯杰老师在深入理解STL源码中所说的vector容器大小1.5倍增长

步骤1

vec.push_back(s);

实验结果:
请添加图片描述
实验结果很显然符合我们的预期,调用push_back函数时,拷贝一次s,此时vector容器大小为1

步骤2

vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
这时候结果开始令人费解了,我们预期应该调用两次拷贝构造函数,为什么这里出现了一次移动构造函数一次析构函数,没关系,这时候我们先记住此时容器大小为2就行,继续下一步

步骤3

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
第三次push_back不出我们所料,令人费解的出现了两次移动构造函数两次析构函数
此时容器大小为3

步骤4

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
此时容器大小为4

步骤5

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
此时容器大小为5
完蛋了,都已经push_back五个元素了,还是令人费解,难道要寄了吗?!最后一次push_back,还不出现转机直接不干了!

步骤6

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
我的天啊,当我们push_back第六个元素的时候出现不一样的结果了,开心!这时候居然只出现了一次拷贝构造函数,此时容器大小是6

步骤7

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

实验结果:
请添加图片描述
该死,第七次又出现了奇奇怪怪的移动构造函数与析构函数
此时容器大小为7

步骤8&9

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

请添加图片描述
第八和第九次都只构造了一次,此时容器大小为9

步骤10

vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);
vec.push_back(s);

结果我就不放了,第十次依然是出现了奇怪的移动构造函数与析构函数
此时容器大小为10
这个时候我回想起之前侯杰老师所讲的vector容器1.5倍增长的结论,心中的疑惑解除了
在这里我们要搞清vec.size()与vec.capacity()的区别

vec.size()vec.capacity()
容器目前容纳的元素个数容器最大容纳元素个数

而且满足vec.size()<=vec.capacity()
一旦vec.size()>vec.capacity()vec会重新分配一块更大的空间,并且容量为之前容量的1.5倍,这就是所谓的1.5倍增长。然而,开辟了新的空间,我们需要把旧空间的元素拷贝到新空间去,但由于旧元素拷贝后就不会再用了,此时我们可以使用移动构造函数让新空间窃取旧空间的元素权限,以减少拷贝所带来的时间开销,同时,旧空间需要被摧毁。
上面的这段操作不就和步骤2、3、4、5、7、10所伴随的奇奇怪怪的移动构造函数析构函数很相似吗,为了描述方便,我将这段操作简述为出现增长

所谓的1.5倍增长指的是vector的最大容纳元素个数!
通过结论,我们可以模拟一下vector增长的过程
这里初始时vector容器大小为1
最大容纳元素个数增长过程:
1 -> 2 -> 3 -> 4 -> 6 -> 9

我们可以模拟一下push_back时候vec的最大容量capacity,就会发现秘密了!

步骤2:出现增长
此时capacity1size1push_backsize=2>capacity=1,需要重新分配空间,并且出现将旧空间1个元素移动到新空间,析构旧空间的1个元素,符合实验结果调用一次移动构造函数与一次析构函数

步骤3:出现增长
此时capacity2size2push_backsize=3>capacity=2,需要重新分配空间,并且出现将旧空间2个元素移动到新空间,析构旧空间的2个元素,符合实验结果调用两次移动构造函数与两次析构函数

步骤4:出现增长
此时capacity3size3push_backsize=4>capacity=3,需要重新分配空间,并且出现将旧空间3个元素移动到新空间,析构旧空间的3个元素,符合实验结果调用三次移动构造函数与三次析构函数

步骤5:出现增长
此时capacity4size4push_backsize=5>capacity=4,需要重新分配空间,并且出现将旧空间4个元素移动到新空间,析构旧空间的4个元素,符合实验结果调用四次移动构造函数与四次析构函数

步骤6:
此时capacity6size5push_backsize=6=capacity=6,不需要重新分配空间,因为最大容纳空间还足以满足分配元素的需求

步骤7:出现增长
此时capacity6size6push_backsize=7>=capacity=6,需要重新分配空间,并且出现将旧空间6个元素移动到新空间,析构旧空间的6个元素,符合实验结果调用六次移动构造函数与六次析构函数

步骤8:
此时capacity9size7push_backsize=8<capacity=9,不需要重新分配空间,因为最大容纳空间还足以满足分配元素的需求

步骤9:
此时capacity9size8push_backsize=9=capacity=9,不需要重新分配空间,因为最大容纳空间还足以满足分配元素的需求

步骤10:出现增长
此时capacity9size9push_backsize=10>capacity=9,需要重新分配空间,并且出现将旧空间9个元素移动到新空间,析构旧空间的9个元素,符合实验结果调用九次移动构造函数与九次析构函数

步骤11:通过实验结果我们可以预测步骤11不会出现增长
此时capacity13size10push_backsize=11<capacity=13

实验结论:

原来啊,步骤2、步骤3、步骤4、步骤5、步骤7、步骤10出现奇奇怪怪的移动与析构函数是因为push_back元素的时候发生了size>capacity,此时需要将最大容纳空间1.5倍扩充,以满足增加元素的需求!至于步骤1、步骤6、步骤8、步骤9因为满足size<=capacity,所以可以直接在旧空间上增加元素,而不需要重新分配一段1.5倍率内存空间

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐