对于STL提供的容器,以前我们用push/insert方法,C++11提供了新的替代方法emplace

#include<iostream>
#include<vector>
using namespace std;

class Test{
public:
Test(int a) { cout << "Test(int)" << endl; }
Test(int a, int b) { cout << "Test(int,int)" << endl; }
Test(const Test& a) { cout << "Test(const Test&)" << endl; }
Test(const Test&& a) { cout << "Test(const Test&&)" << endl; }
~Test() { cout << "~Test()" << endl; }
};

int main(){
Test t1(10); //左值
vector<Test> v;
v.reserve(100); //只开辟空间,没有构建对象
cout << "=====================" << endl;
v.push_back(t1); //匹配带左值引用参数的拷贝构造函数
v.emplace_back(t1); //匹配带左值引用参数的拷贝构造函数
cout << "=====================" << endl;

return 0;
}

C++11提供STL的emplace方法剖析一_拷贝构造函数


如果直接插入对象,emplace_back和push_back是没有区别的,都是对象的调用拷贝构造

int main(){
Test t1(10); //左值
vector<Test> v;
v.reserve(100); //只开辟空间,没有构建对象
cout << "=====================" << endl;
v.push_back(t1); //匹配带左值引用参数的拷贝构造函数
v.emplace_back(t1); //匹配带左值引用参数的拷贝构造函数
cout << "=====================" << endl;
v.push_back(Test(20)); //匹配带右值引用参数的拷贝构造函数
v.emplace_back(Test(20)); //匹配带右值引用参数的拷贝构造函数
cout << "=====================" << endl;
return 0;
}

C++11提供STL的emplace方法剖析一_开发语言_02


如果是插入对象,不管是左值还是右值,都需要先调用构造函数生成对象,再调用拷贝构造在容器底层空间构造对象

int main(){
vector<Test> v;
v.push_back(Test(10, 20)); // 右值拷贝构造,引用方式传递,临时对象析构后,容器底层的对象数据也不对
cout << "=====================" << endl;
v.emplace_back(11, 21);
cout << "=====================" << endl;
cout << v[0].a_ << " " << v[0].b_ << endl;
cout << v[1].a_ << " " << v[1].b_ << endl;
return 0;
}
Test(int,int)                 // 构造临时对象
Test(const Test&&) // 扩容时,先把临时对象拷贝到新容器的空间
~Test() // 临时对象析构
=====================
Test(int,int) // 先在(新容器起始地址 + 旧容器空间大小) 处构造新的对象
Test(const Test&) // 再把旧容器元素拷贝到新容器起始地址
~Test() // 旧容器对象析构
=====================
-842150451 -842150451 // 由于容器中扩容后插入的是临时对象,匹配右值拷贝构造,不是左值,并没有在容器空间构造新对象,临时对象析构后,容器里的数也成了随机数
11 21 // 直接在容器空间构造,和其他的变量无关,生命周期由容器管理,不会变成随机值
~Test()
~Test()

对于emplace而言,如果传入要构建Test对象所需要的参数(构造函数的参数),就会直接在vector底层构造对象,不会产生额外的临时对象。而对于push_back来说,是先构造出临时对象,再进行拷贝构造

int main(){
unordered_map<int, string>map;
map.insert(make_pair(11, "hello"));
// 构造piar临时对象,然后拿临时对象调用右值引用参数的拷贝构造函数构造map上的对象
// 在map底层构建好对象后,出了这条语句,临时对象析构。

map.emplace(12, "world");
// 直接传入构建对象所需要的参数,在map底层调用普通构造函数生成对象
// 没有产生临时对象等额外的对象,没有额外的函数的调用
return 0;
}