C++霧中風(fēng)景9:emplace_back與可變長模板

C++11的版本在vector容器添加了emplace_back方法,相對于原先的push_back方法能夠在一定程度上提升vector容器的表現(xiàn)性能。所以我們從STL源碼角度來切入,看看這兩種方法有什么樣的區(qū)別,新引進(jìn)的方法又有什么可學(xué)習(xí)參考之處。

1.emplace_back的用法emplace_back方法最大的改進(jìn)就在與可以利用類本身的構(gòu)造函數(shù)直接在內(nèi)存之中構(gòu)建對象,而不需要調(diào)用類的拷貝構(gòu)造函數(shù)與移動構(gòu)造函數(shù)。舉個栗子,假設(shè)如下定義了一個時間類time,該類同時定義了拷貝構(gòu)造函數(shù)與移動構(gòu)造函數(shù):

class time {

private:

?int hour;?

?int minute;?

?int second;

public:?

?time(int h, int m, int s) :hour(h), minute(m), second(s) {?

?}?

?time(const time& t) :hour(t.hour), minute(t.minute), second(t.second) {?

?cout << "copy" << endl;?

?}?

?time(const time&& t) noexcept:hour(t.hour),minute(t.minute),second(t.second) {

?cout << "move" << endl;

?}

};

在main方法之中執(zhí)行下面的代碼邏輯:

intmain(){

? ? vector tlist;

? ? timet(1,2,3);

? ? tlist.emplace_back(t);

? ? tlist.emplace_back(2, 3, 4);? //直接調(diào)用了time的構(gòu)造函數(shù)在vector的內(nèi)存之中建立起新的對象? ? getchar();

}

執(zhí)行結(jié)果: copy?

?move (這次拷貝構(gòu)造函數(shù)的調(diào)用是因為vector本身的擴(kuò)容,也就是移動之前的已經(jīng)容納的time對象)

由上述代碼我們看到time對象可以直接利用emplace_back方法在vector上構(gòu)造對象,通過這樣的方式來減少不必要的內(nèi)存操作。(省去了拷貝構(gòu)造的環(huán)節(jié))。同樣的在main之中執(zhí)行下面的代碼邏輯:

int main(){ vector tlist;

? ? time t(1, 2, 3);

? ? tlist.emplace_back(move(t)); //調(diào)用move函數(shù)使time對象成為右值,可以利用移動構(gòu)造函數(shù)來拷貝對象

? ? tlist.emplace_back(2, 3, 4);? //直接調(diào)用了time的構(gòu)造函數(shù)在vector的內(nèi)存之中建立起新的對象

? ? getchar();

}

執(zhí)行結(jié)果:

? move? ? ? ? ? ? ? ? ?

? move (這次拷貝構(gòu)造函數(shù)的調(diào)用是因為vector本身的擴(kuò)容,也就是移動之前的已經(jīng)容納的time對象)

通過這樣的方式也減少不必要的內(nèi)存操作。(省去了移動構(gòu)造的環(huán)節(jié))。所以這就是為什么在C++11之后提倡大家使用emplace_back來代替舊代碼之中的push_back函數(shù)。如下面的代碼所示,在push_back底層也是調(diào)用了emplace_back來實現(xiàn)對應(yīng)的操作流程:

void push_back(const _Ty& _Val) {?

? ? ? emplace_back(_Val);

}

void push_back(_Ty&& _Val) {? ?

? ? ? emplace_back(_STD move(_Val));

}

2.emplace_back的實現(xiàn)源碼面前,了無秘密,接下來跟隨筆者直接來看看emplace_back的源代碼,來引出我們今天的主題:public: template decltype(auto) emplace_back(_Valty&&... _Val)

? ? ? ? {? // insert by perfectly forwarding into element at end, provide strong guarantee

? ? ? ? if (_Has_unused_capacity())

? ? ? ? ? ? {

? ? ? ? ? ? _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...);

? ? ? ? ? ? }

? ? ? ? else

? ? ? ? ? ? {? // reallocate

? ? ? ? ? ? const size_type _Oldsize = size();

? ? ? ? ? ? if (_Oldsize == max_size())

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? _Xlength();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? const size_type _Newsize = _Oldsize + 1;

? ? ? ? ? ? const size_type _Newcapacity = _Calculate_growth(_Newsize);

? ? ? ? ? ? bool _Emplaced = false;

? ? ? ? ? ? const pointer _Newvec = this->_Getal().allocate(_Newcapacity);

? ? ? ? ? ? _Alty& _Al = this->_Getal();

? ? ? ? ? ? _TRY_BEGIN

? ? ? ? ? ? _Alty_traits::construct(_Al, _Unfancy(_Newvec + _Oldsize), _STD forward<_Valty>(_Val)...);

? ? ? ? ? ? _Emplaced = true;

? ? ? ? ? ? _Umove_if_noexcept(this->_Myfirst(), this->_Mylast(), _Newvec);

? ? ? ? ? ? _CATCH_ALL

? ? ? ? ? ? if (_Emplaced)

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? _Alty_traits::destroy(_Al, _Unfancy(_Newvec + _Oldsize));

? ? ? ? ? ? ? ? }

? ? ? ? ? ? _Al.deallocate(_Newvec, _Newcapacity);

? ? ? ? ? ? _RERAISE;

? ? ? ? ? ? _CATCH_END

? ? ? ? ? ? _Change_array(_Newvec, _Newsize, _Newcapacity);

? ? ? ? ? ? }

#if _HAS_CXX17

? ? ? ? return (this->_Mylast()[-1]);

#endif /* _HAS_CXX17 */

? ? ? ? }

通過上述代碼可以看到,emplace_back的流程邏輯很簡單。先檢查vector的容量,不夠的話就擴(kuò)容,之后便通過**_Alty_traits::construct來創(chuàng)建對象。而最終利用強(qiáng)制類似裝換的指針來指向容器類之中對應(yīng)類的構(gòu)造函數(shù),并且利用可變長模板**將構(gòu)造函數(shù)所需要的內(nèi)容傳遞過去構(gòu)造新的對象。templatestatic void construct(_Alloc&, _Objty * const _Ptr, _Types&&... _Args) { // construct _Objty(_Types...) at _Ptr ::new (const_cast(static_cast(_Ptr)))

? ? ? ? ? ? _Objty(_STD forward<_Types>(_Args)...);

? ? ? ? }

emplace_back這里最為巧妙的部分就是利用可變長模板實現(xiàn)了,任意傳參的對象構(gòu)造。可變長模板是C++11新引進(jìn)的特性,接下來我們來詳細(xì)看看可變長模板是如何來使用,來實現(xiàn)任意長度的參數(shù)呢?

3.可變長模板與函數(shù)式編程首先,我們先看看,可變長模板的定義: template void f(T... args);

通過template來聲明參數(shù)包args,這個參數(shù)包中可以包含0到任意個參數(shù),并且作為函數(shù)參數(shù)調(diào)用。之后我們便可以在函數(shù)之中將參數(shù)包展開成一個一個獨立的參數(shù)。

假設(shè)我們有如下需求,需要定義一個max_num函數(shù)來求出一組任意參數(shù)數(shù)字的最大值,在C++11之前的版本或許需要這樣去定義這個函數(shù),也就是說我們需要一個參數(shù)來指定對應(yīng)參數(shù)的個數(shù),并且這個過程之中存在參數(shù)的類型不一致的潛在風(fēng)險,并不能在編譯期進(jìn)行反饋(不能在編譯期進(jìn)行對于動態(tài)語言來說根本不是什么大不了的問題,囧rz):


通過不斷遞歸的方式,提取可變長模板參數(shù)之中的首個元素,并且設(shè)置遞歸的終止點的方式來依次處理各個元素。這種處理函數(shù)的方式本質(zhì)上就是在通過遞歸的方式處理列表,這種編程思路在函數(shù)式編程語言之中十分常見,在C++之中看到這樣的用法,也讓筆者作為C++的入門選手感到很新奇。筆者曾經(jīng)接觸過Scala與Erlang語言之中大量利用了這種寫法,但是多層遞歸導(dǎo)致的必然是棧調(diào)用的開銷變大,利用尾遞歸的方式來優(yōu)化這樣的寫法,才能減少非必要的函數(shù)調(diào)用開銷。

4.小結(jié)

由emplace_back引申出來不少對C++11新特性的探索,筆者也僅僅做一些拋磚引玉的工作。作為程序員,希望大家能夠堅持不斷動態(tài)更新對語言的學(xué)習(xí)與探索來凝練與高效率的Coding,這也是筆者堅持更新該系列文章的初衷。

喜歡小編的希望多多關(guān)注我,久伴帶你了解C++風(fēng)景。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內(nèi)容