在 C++11
之后,vector
容器中添加了新的方法:emplace_back()
,和 push_back()
一样的是都是在容器末尾添加一个新的元素进去,不同的是 emplace_back()
在效率上相比较于 push_back()
有了一定的提升。
同样是在容器尾部加入一个元素,push_back
和 emplace_back
是不一样的;
push_back
push_back函数的参数实际上是对应类型的左值或右值引用。传递的参数都会被形参直接引用,然后会直接通过拷贝构造或者移动构造在容器末尾增加一个元素。
但是如果在传参处构建临时对象传入,则过程就会如下
- 构造一个临时对象
- 调用移动构造函数把临时对象的副本拷贝到容器末尾增加的元素中
调用析构释放临时对象
假如它的元素类型有单参数的构造函数构造函数,可以直接给 push_back 传递单个参数
例如class Test{ public: Test(int a) { cout << "Test(int a)" << endl; this->a = a; } Test(int a, int b) { cout << "Test(int a)" << endl; this->a = a; } Test(const Test&) { cout << "Test(const Test&)" << endl; } Test(Test&&) { cout << "Test(Test&&)" << endl; } ~Test() { cout << "~Test()" << endl; } }; int main() { vector<Test> vec; vec.push_back(1); // 这里实际上是发生了隐式类型转换 vec.push_back({1,2}); // 这种也是发生了隐式类型转换 }
这两个push_back就会报错, 因为禁用了隐式类型转换
当我们想调用多参数的情况时,就不可以直接传递了, 因为 push_back 只接受单个参数, 我们必须在此处创建临时对象传入
例如
vec.push_back(Test{1, 2});
vec.push_back(Test(1, 2));
这样就避免不了临时对象构造和析构的过程。
emplace_back
emplace_back 的参数实际上是一个模板参数包, 他直接将构造元素的参数传递给对应的构造函数, 所以就少了创建一个临时对象的过程,比如像传递多个构造函数的参数, emplace_back 就可以这样
vec.emplace_back(1, 2);
因为 emplace_back是把多个参数作为一个整体, 传递给对应的构造函数了
emplace_back 的过程
- 调用构造函数在容器末尾增加一个元素
同样是在容器尾部增加一个元素,emplace_back
在直接传递参数创建元素时比 push_back
少了一次临时对象的构造和析构以及移动构造, 所以,emplace_back
相比 push_back
高效一些。
验证一下
还是Test这个类
class Test{
public:
Test(int a) {
cout << "Test(int a)" << endl;
this->a = a;
}
Test(const Test&) {
cout << "Test(const Test&)" << endl;
}
Test(Test&&) {
cout << "Test(Test&&)" << endl;
}
~Test() {
cout << "~Test()" << endl;
}
};
- 用已经存在的对象插入 vector
push_back
vector<Test> vec; Test a(1); vec.push_back(a);
输出如下(忽略 a 对象以及vec析构时调用元素的析构)
Test(const Test&)
可以看到只有一次拷贝构造
emplace_back
输出如下(忽略 a 对象以及vec析构时调用元素的析构)Test(const Test&)
同样只有一次拷贝构造
- 新创建对象插入
push_back
vector<Test> vec; vec.push_back(1); // 隐式转换, 会创建临时对象, 等价于 vec.push_back(Test(1))
输出
Test(int a) 临时对象构造 Test(Test&&) ~Test() 临时对象析构
emplace_back
vector<Test> vec; vec.emplace_back(1);
输出
Test(int a) 直接将参数传递给构造函数了, 没有临时对象产生
总结
当我们想把一个已经存在的对象插入 vector 时, 使用二者都是相似的, 都只会调用一次拷贝或移动构造,
但是我们想在 vector 添加一个本不存在的对象时,二者会有一些不同,push_back 不可避免的多了一次临时对象的创建和销毁,以及将临时对象移动到 vector 中的开销, 而 emplace_back 则可以在容器内直接构造。