已经过了8个月了。来更新一下这篇文章。在写这篇文章的时候对rvalue还不懂,所以没有意识到这里对于class A是否定义了move constructor会得到不同的结果。感谢评论区的大佬指出了其中的错误/不严谨的地方。在这儿更新一下当class A被定义了move constructor的情况。
#include <vector>
#include <iostream>
#include <iterator>
using namespace std;
int constructorCount = 0;
int copyConstructorCount = 0;
int destructorCount = 0;
int moveCntrCount = 0;
class A
{
public:
A() {
constructorCount++;
}
A(const A& src) {
copyConstructorCount++;
}
A(A && src) {
moveCntrCount++;
}
~A() {
destructorCount++;
}
};
int main()
{
vector<A> v;
for (int i = 0; i < 10; ++i) {
v.push_back(A());
}
cout << "Constructor: " << constructorCount << endl;
cout << "Copy Constructor: " << copyConstructorCount << endl;
cout << "Move Constructor: " << moveCntrCount << endl;
cout << "Destructor: " << destructorCount << endl;
return 0;
}
输出结果为
Constructor: 10
Copy Constructor: 15
Move Constructor: 10
Destructor: 25
唯一的区别是从copy constructor被call 25次,变成了move constructor被call 10次 + copy constructor被call 15次。因为 v.push_back(A())中A()是rvalue,所以引发了move constructor,在10次新建过程中,都触发的是move constructor。但是在reallocation的时候还是会触发copy constructor。原因在于reallocation的原因就在于原有的空间不够用,要移动到新的空间。而move constructor其实就是修改地址而不进行实际的copy,所以在原有空间不够用的情况下,并无计可施,只能老老实实用copy constructor进行copy (一共15次)。
对于rvalue的笔记也在专栏中更新,欢迎指正:
法号桑菜:lvalue, rvalue & move semanticszhuanlan.zhihu.com————————分割线——————————————————————
感谢评论区的很多大佬给我提了很多意见。让我发现在对class A的不同的定义情况下,会产生不同的结果。我重新更新了完整的题目代码,所以以下的问题只针对这个代码。有兴趣的读者也可以到评论区看我推荐的大佬的回复,在定义move constructor的情况下,是什么样的结果。
--------------分割线---------------------------------------------------
分享一个两年前面试一家顶级HFT(传言HFT中bar最高的)挂掉的C++题目。
题目是这样的,给下面一下段代码,问constructor, copy constructor, 以及destructor分别被使用了多少次。
#include <vector>
#include <iostream>
#include <iterator>
using namespace std;
int constructorCount = 0;
int copyConstructorCount = 0;
int destructorCount = 0;
class A
{
public:
A() {
constructorCount++;
}
A(const A& src) {
copyConstructorCount++;
}
~A() {
destructorCount++;
}
};
int main()
{
vector<A> v;
for (int i = 0; i < 10; ++i) {
v.push_back(A());
}
cout << constructorCount << " " << copyConstructorCount << " " << destructorCount << endl;
return 0;
}
要想弄清楚这个问题,最重要的就是知道push_back()发生了什么。
我们分两种情况考虑。第一种,v的capacity足够大,这里会先通过constructor构造一个class A object,然后将它用copy constructor复制到v的末尾,最后原先通过constructor构造的object会被destructor销毁掉。这种情况下,constructor,copy constructor以及destructor各被使用了一次。
第二种情况,v的capacity到了极限,这时候需要reallocate(对c++ vector capacity/reallocation不了解的读者可以先去学习一下相关知识)。这里还是会先通过constructor构造一个class A object,然后发现capacity不够大了,于是重新reallocate新的空间,把这个新的object用copy constructor复制到新的空间的相应位置。当然这个object被销毁掉也需要使用一次destructor。到这里为止,都和第一种情况是一样的。但是因为重新allocate了空间,所以我们还必须要把原有的object都复制到新的空间,并把旧的空间中的object销毁掉。这里就会额外使用x次copy constructor和destructor, x为原来capacity的大小。
于是这个问题就基本解决了。reallocation每次都会double capacity,额外的计算会发生在
capacity 0->1 (使用了0次copy constructor和destructor)
capacity 1->2 (使用了1次copy constructor和destructor)
capacity 2->4 (使用了2次copy constructor和destructor)
capacity 4->8 (使用了4次copy constructor和destructor)
capacity 8->16 (使用了8次copy constructor和destructor)
0+1+2+4+8=15
一共10个元素,总计使用了10次constructor,10+15=25次copy constructor和10+15=25次destructor。