C++11中大部分的容器對于添加元素除了傳統(tǒng)的 insert
或者 pusb_back/push_front
之外都提供一個新的函數(shù)叫做 emplace
。 比如如果你想要向 vector 的末尾添加一個數(shù)據(jù)
nums.push_back(1);
nums.empace_back(1);
臨時變量
emplace
相較于insert
最大的作用是避免產(chǎn)生不必要的臨時變量。舉例說明:
/// 定義結(jié)構(gòu)體可以使用大括號法構(gòu)造對象
struct Obj{
Obj(int a, int b);
};
vector<Obj> v;
v.emplace(Obj.begin(), 0,0); /// 沒有臨時變量產(chǎn)生
v.insert(Obj.begin(), obj(0, 0)); /// 需要產(chǎn)生一個臨時變量
v.insert(Obj.begin(), {0, 0}); /// 需要產(chǎn)生一個臨時變量
emplace
的語法看起來比較特別贝攒,后面兩個參數(shù)自動用來構(gòu)造 vector 內(nèi)部的 Obj對象誓禁。這是因為其內(nèi)部利用了 C++11 的兩個新特性 變參模板
和 完美轉(zhuǎn)發(fā)
井佑。
- 變參模板: 函數(shù)可以接受任意參數(shù)绰播,適用于任意對象的構(gòu)建仗颈。
- 完美轉(zhuǎn)發(fā): 使得接收下來的參數(shù)能夠原樣完美地傳遞給對象的構(gòu)造函數(shù)黍瞧,這帶來另一個方便性就是即使是構(gòu)造函數(shù)聲明為 explicit 它還是可以正常工作诸尽,因為它不存在臨時變量和隱式轉(zhuǎn)換。
性能區(qū)別
-
insert印颤、push_back
之所以慢的原因在與創(chuàng)建臨時對象時,需要申請內(nèi)存空間, 申請內(nèi)存空間一向是耗時很嚴重的操作;之后再通過拷貝構(gòu)造函數(shù)把創(chuàng)建的臨時對象復制到vector空間中您机,期間的復制操作也是需要CPU時間的; -
emplace_back
之所以快的其原因是直接在vector中已有的空間上, 調(diào)用了構(gòu)造函數(shù), 節(jié)省了臨時對象的內(nèi)存空間申請以及移動構(gòu)造函數(shù)的復制操作.
emplace實現(xiàn)原理
-
push_back
先向容器尾部添加一個右值元素(臨時對象)
,然后調(diào)用構(gòu)造函數(shù)構(gòu)造出這個臨時對象年局,最后調(diào)用移動構(gòu)造函數(shù)
將這個臨時對象放入容器中并釋放這個臨時對象际看。
注:最后調(diào)用的不是拷貝構(gòu)造函數(shù),而是移動構(gòu)造函數(shù)矢否。因為需要釋放臨時對象仲闽,所以通過std::move
進行移動構(gòu)造,可以避免不必要的拷貝操作 -
emplace_back
在容器尾部添加一個元素僵朗,調(diào)用構(gòu)造函數(shù)原地構(gòu)造赖欣,不需要觸發(fā)拷貝構(gòu)造和移動構(gòu)造屑彻。因此更加高效。
三種構(gòu)造函數(shù)的聲明方式:
/// 構(gòu)造函數(shù)
Student(Student&& s) : name(std::move(s.name)), age(s.age);
/// 拷貝構(gòu)造
Student(const Student& s) : name(std::move(s.name)), age(s.age);
/// 移動構(gòu)造函數(shù)
Student(string&& n, int a) : name(std::move(n)), age(a);
emplace_back函數(shù)顶吮,它通過完美轉(zhuǎn)發(fā)實現(xiàn)了在vector中插入時直接在容器內(nèi)構(gòu)造對象社牲,省略了創(chuàng)建臨時對象的操作。我們看下它的代碼就不難理解.
template<class _Ty,
class _Alloc = allocator<_Ty>>
class vector
: public _Vector_alloc<_Vec_base_types<_Ty, _Alloc>>
{
...
public:
template<class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val)
{ // insert by perfectly forwarding into element at end, provide strong guarantee
if (_Has_unused_capacity())
{
return (_Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...));
}
_Ty& _Result = *_Emplace_reallocate(this->_Mylast(), _STD forward<_Valty>(_Val)...);
return (_Result);
}
...
};
對于上面案例中的vector<string>來說悴了,_Ty是string搏恤,調(diào)用vector.emplace_back("6666"),則_Valty就是const char*让禀,通過完美轉(zhuǎn)發(fā)機制(forward<_Valty>)
最終將傳入的參數(shù)_Val(本例中就是"6666")傳入string的構(gòu)造函數(shù)中挑社,實現(xiàn)了直接從list中一步到位構(gòu)造對象,省略了創(chuàng)建臨時對象的過程巡揍,從而減少了創(chuàng)建的時間痛阻。