转载
例子:
#include<iostream>
#include <vector>#include <fstream>
using namespace std;
ofstream out("1.txt");
class Rock{
public:
Rock(){out <<"Rock()"<<endl;}
Rock(const Rock&){out <<"const Rock&()"<<endl;}
~Rock(){out <<"~Rock()"<<endl;}
};
int main()
{
Rock a;
Rock b;
Rock c;
vector<Rock>v;
v.push_back(a);
v.push_back(b);
v.push_back(c);
return 0;
}
运行结果为:
Rock()
Rock()
Rock()
const Rock&()
const Rock&()
const Rock&()
~Rock()
const Rock&()
const Rock&()
const Rock&()
~Rock()
~Rock()
~Rock()
~Rock()
~Rock()
~Rock()
~Rock()
~Rock()
FROM: http://bbs.csdn.net/topics/390396294?page=1
=============================================================
本文同步自:http://zohead.com/archives/vector-push-back-space-copy/
这两天在实际程序中使用 STL 的 vector push_back 类对象时出现问题,偶尔发现 vector 在 push_back 时的调用类对象的拷贝构造函数和析构函数有点特别,简单做下分析。
程序代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
#include <iostream>
#include <vector>
using
namespace
std;
struct
sss
{
public
:
explicit
sss(
int
val) : value(val)
{
cout <<
"---init sss "
<<
this
<<
", value:"
<< value << endl;
}
sss(
const
sss& org)
{
cout <<
"---copy "
<< &org <<
" to "
<<
this
<< endl;
value = org.value;
}
~sss()
{
cout <<
"---destory sss "
<<
this
<<
", value:"
<< value << endl;
}
int
value;
};
int
main(
int
argc,
char
** argv)
{
sss s_tmp(11);
int
i = 0;
vector<sss> vvv;
for
(i = 0; i < 5; i++) {
s_tmp.value++;
vvv.push_back(s_tmp);
cout <<
"size: "
<< vvv.size() <<
", capacity: "
<< vvv.capacity() << endl;
}
return
0;
}
|
功能很简单,main 中定义一个 sss 类对象和对应的 vector,然后在循环中改类成员的值,并依次 push_back 到 vector 中,类的构造函数、析构函数、拷贝构造函数中都加了对应的打印输出。循环运行了5次,往 vector 中增加了5个类成员。
实际运行输出如下:
---init sss 0x22ff20, value:11
---copy 0x22ff20 to 0x5d2a58
size: 1, capacity: 1
---copy 0x5d2a58 to 0x5d2ad8
---copy 0x22ff20 to 0x5d2adc
---destory sss 0x5d2a58, value:12
size: 2, capacity: 2
---copy 0x5d2ad8 to 0x5d2ae8
---copy 0x5d2adc to 0x5d2aec
---copy 0x22ff20 to 0x5d2af0
---destory sss 0x5d2ad8, value:12
---destory sss 0x5d2adc, value:13
size: 3, capacity: 4
---copy 0x22ff20 to 0x5d2af4
size: 4, capacity: 4
---copy 0x5d2ae8 to 0x5d2b00
---copy 0x5d2aec to 0x5d2b04
---copy 0x5d2af0 to 0x5d2b08
---copy 0x5d2af4 to 0x5d2b0c
---copy 0x22ff20 to 0x5d2b10
---destory sss 0x5d2ae8, value:12
---destory sss 0x5d2aec, value:13
---destory sss 0x5d2af0, value:14
---destory sss 0x5d2af4, value:15
size: 5, capacity: 8
---destory sss 0x5d2b00, value:12
---destory sss 0x5d2b04, value:13
---destory sss 0x5d2b08, value:14
---destory sss 0x5d2b0c, value:15
---destory sss 0x5d2b10, value:16
---destory sss 0x22ff20, value:16
结果分析:
vector 每次调用 push_back 时都会拷贝一个新的参数指定的 sss 类对象,这会调用 sss 的拷贝构造函数,第一次的 copy 正常,而且 vector 的实际容量也由 0 变为 1。
第二次调用 push_back,通过输出会发现调用了两次拷贝构造函数,一次析构函数,原来 vector 此时判断容量不够,将容量扩大为原来的两倍,变为 2,并将原来的元素再次拷贝一份存放到新的内存空间,然后拷贝新加的类对象,最后再释放原来的元素。
第三次调用 push_back 时,vector 自动扩大为4,因此拷贝构造函数调用了3次,析构函数调用了2次,程序最终退出了时就析构了 5 次加本身的 sss 类对象一共 6 次。
参考:
由此看来,vector 的 push_back 在发现空间不足时自动将空间以 2 的指数增长:0 -> 1 -> 2 -> 4 -> 8 -> 16 -> 32 ...
查找资料后得知,如此设计的主要目的是为了尽可能的减小时间复杂度;如果每次都按实际的大小来增加 vector 的空间,会造成时间复杂度很高,降低 push_back 的速度。
另外关于 push_back 为什么会执行拷贝构造函数,push_back 的原型为:
void push_back(const _Ty& _Val)
参数是以引用方式传递,按说不会拷贝,但 push_back 实际实现中判断空间不足时是调用 insert 函数添加元素:
void push_back(const _Ty& _Val)
{
// insert element at end
if (size() < capacity())
#if _HAS_ITERATOR_DEBUGGING
{
// room at end, construct it there
_Orphan_range(_Mylast, _Mylast);
_Mylast = _Ufill(_Mylast, 1, _Val);
}
#else /* _HAS_ITERATOR_DEBUGGING */
_Mylast = _Ufill(_Mylast, 1, _Val);
#endif /* _HAS_ITERATOR_DEBUGGING */
else
insert(end(), _Val);
}
更新:
2012-05-10:
近期在 Visual Studio 2010 中发现 vector 的实际空间增加顺序为:1 - 2 - 3 - 4 - 6 - 9 - 13 - 19 - 28 - 42 - 63 - 94 - 141 - 211 ...,有空时再继续研究。
以上只是个人粗略分析,有任何问题欢迎指正,玩的开心咯 ^_^
from: http://zohead.com/archives/vector-push-back-space-copy/