我们都知道std :: vector会动态增加它的容量。例如一个向量在开始时容量= 0,通过推回元素,它的容量逐渐增加到1,2,4,8 ......
每次增加容量时,都必须将旧元素复制/移动到新创建的内存数组中。
我的问题是,当元素类同时具有复制构造函数和rvalue构造函数(用于移动语义)时,std :: vector将更喜欢复制构造函数而不是rvalue构造函数。这是为什么?因为很明显没有人需要旧的变量。
以下是一个例子:
#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
class foo {
public:
foo()
: id(-1)
, c(nullptr)
{
std::cout << "foo()\n";
}
foo(int i)
: id(i)
{
c = new char[100000000];
std::cout << "foo(" << i << ")\n";
}
// foo(const foo& rhs)
// {
// if (rhs.c)
// {
// id = rhs.id;
// std::cout << "copy " << id << std::endl;
// c = new char[100000000];
// std::memcpy(c, rhs.c, 100000000);
// }
// }
foo(foo&& rhs)
{
id = rhs.id;
std::cout << "move " << id << std::endl;
c = rhs.c;
rhs.c = nullptr;
}
~foo()
{
if (c)
{
delete [] c;
std::cout << "desctruct " << id << std::endl;
}
}
public:
char* c;
int id = -1;
};
std::vector<foo> getFoo()
{
std::vector<foo> vec;
// vec.reserve(3);
for (int i = 0; i < 3; ++i)
{
foo f(i);
vec.push_back(std::move(f));
std::cout << "capacity: " << vec.capacity() << std::endl;
}
return vec;
}
int main(int argc, char *argv[])
{
using namespace std::chrono;
auto tic = high_resolution_clock::now();
auto&& vec = getFoo();
std::cout << "vec size: " << vec.size() << std::endl;
auto toc = high_resolution_clock::now();
std::cout << duration_cast<microseconds>(toc - tic).count() << "us" << std::endl;
return 0;
}
如您所见,如果我注释掉复制构造函数,则只涉及移动操作:
foo(0)
move 0
capacity: 1
foo(1)
move 1
move 0
capacity: 2
foo(2)
move 2
move 1
move 0
capacity: 4
vec size: 3
165us
desctruct 2
desctruct 1
desctruct 0
但是,如果我取消注释复制构造函数,那么会有很多复制操作,这非常昂贵:
foo(0)
move 0
capacity: 1
foo(1)
move 1
copy 0
desctruct 0
capacity: 2
foo(2)
move 2
copy 1
copy 0
desctruct 1
desctruct 0
capacity: 4
vec size: 3
1008324us
desctruct 2
desctruct 1
desctruct 0
如何避免此类复制操作?