C++11 make_shared

make_shared的使用:


shared_ptr<string> p1 = make_shared<string>(10, '9');  
 
shared_ptr<string> p2 = make_shared<string>("hello");  
 
shared_ptr<string> p3 = make_shared<string>(); 

盡量使用make_shared初始化

C++11 中引入了智能指針, 同時(shí)還有一個(gè)模板函數(shù) std::make_shared 可以返回一個(gè)指定類型的 std::shared_ptr, 那與 std::shared_ptr 的構(gòu)造函數(shù)相比它能給我們帶來(lái)什么好處呢 ?

make_shared初始化的優(yōu)點(diǎn)

1胁出、提高性能

shared_ptr 需要維護(hù)引用計(jì)數(shù)的信息:
強(qiáng)引用, 用來(lái)記錄當(dāng)前有多少個(gè)存活的 shared_ptrs 正持有該對(duì)象. 共享的對(duì)象會(huì)在最后一個(gè)強(qiáng)引用離開的時(shí)候銷毀( 也可能釋放).
弱引用, 用來(lái)記錄當(dāng)前有多少個(gè)正在觀察該對(duì)象的 weak_ptrs. 當(dāng)最后一個(gè)弱引用離開的時(shí)候, 共享的內(nèi)部信息控制塊會(huì)被銷毀和釋放 (共享的對(duì)象也會(huì)被釋放, 如果還沒有釋放的話).
如果你通過(guò)使用原始的 new 表達(dá)式分配對(duì)象, 然后傳遞給 shared_ptr (也就是使用 shared_ptr 的構(gòu)造函數(shù)) 的話, shared_ptr 的實(shí)現(xiàn)沒有辦法選擇, 而只能單獨(dú)的分配控制塊:


使用shred_ptr初始化

如果選擇使用 make_shared 的話, 情況就會(huì)變成下面這樣:


使用make_shared

std::make_shared(比起直接使用new)的一個(gè)特性是能提升效率辆童。使用std::make_shared允許編譯器產(chǎn)生更小,更快的代碼,產(chǎn)生的代碼使用更簡(jiǎn)潔的數(shù)據(jù)結(jié)構(gòu)。考慮下面直接使用new的代碼:

std::shared_ptr<Widget> spw(new Widget);

很明顯這段代碼需要分配內(nèi)存,但是它實(shí)際上要分配兩次。每個(gè)std::shared_ptr都指向一個(gè)控制塊暇检,控制塊包含被指向?qū)ο蟮囊糜?jì)數(shù)以及其他東西产阱。這個(gè)控制塊的內(nèi)存是在std::shared_ptr的構(gòu)造函數(shù)中分配的。因此直接使用new块仆,需要一塊內(nèi)存分配給Widget构蹬,還要一塊內(nèi)存分配給控制塊。

如果使用std::make_shared來(lái)替換

auto spw = std::make_shared<Widget>();

一次分配就足夠了悔据。這是因?yàn)閟td::make_shared申請(qǐng)一個(gè)單獨(dú)的內(nèi)存塊來(lái)同時(shí)存放Widget對(duì)象和控制塊庄敛。這個(gè)優(yōu)化減少了程序的靜態(tài)大小,因?yàn)榇a只包含一次內(nèi)存分配的調(diào)用科汗,并且這會(huì)加快代碼的執(zhí)行速度藻烤,因?yàn)閮?nèi)存只分配了一次。另外头滔,使用std::make_shared消除了一些控制塊需要記錄的信息怖亭,這樣潛在地減少了程序的總內(nèi)存占用。

對(duì)std::make_shared的效率分析可以同樣地應(yīng)用在std::allocate_shared上坤检,所以std::make_shared的性能優(yōu)點(diǎn)也可以擴(kuò)展到這個(gè)函數(shù)上兴猩。

2、 異常安全

我們?cè)谡{(diào)用processWidget的時(shí)候使用computePriority()早歇,并且用new而不是std::make_shared:

processWidget(std::shared_ptr<Widget>(new Widget),  //潛在的資源泄露 
              computePriority());

就像注釋指示的那樣倾芝,上面的代碼會(huì)導(dǎo)致new創(chuàng)造出來(lái)的Widget發(fā)生泄露。那么到底是怎么泄露的呢箭跳?調(diào)用代碼和被調(diào)用函數(shù)都用到了std::shared_ptr晨另,并且std::shared_ptr就是被設(shè)計(jì)來(lái)阻止資源泄露的。當(dāng)最后一個(gè)指向這兒的std::shared_ptr消失時(shí)谱姓,它們會(huì)自動(dòng)銷毀它們指向的資源拯刁。如果每個(gè)人在每個(gè)地方都使用std::shared_ptr,那么這段代碼是怎么導(dǎo)致資源泄露的呢逝段?

答案和編譯器的翻譯有關(guān)垛玻,編譯器把源代碼翻譯到目標(biāo)代碼,在運(yùn)行期奶躯,函數(shù)的參數(shù)必須在函數(shù)被調(diào)用前被估值帚桩,所以在調(diào)用processWidget時(shí),下面的事情肯定發(fā)生在processWidget能開始執(zhí)行之前:

表達(dá)式“new Widget”必須被估值嘹黔,也就是账嚎,一個(gè)Widget必須被創(chuàng)建在堆上莫瞬。
std::shared_ptr(負(fù)責(zé)管理由new創(chuàng)建的指針)的構(gòu)造函數(shù)必須被執(zhí)行。
computePriority必須跑完郭蕉。
編譯器不需要必須產(chǎn)生這樣順序的代碼疼邀。但“new Widget”必須在std::shared_ptr的構(gòu)造函數(shù)被調(diào)用前執(zhí)行,因?yàn)閚ew的結(jié)構(gòu)被用為構(gòu)造函數(shù)的參數(shù)召锈,但是computePriority可能在這兩個(gè)調(diào)用前(后旁振,或很奇怪地,中間)被執(zhí)行涨岁。也就是拐袜,編譯器可能產(chǎn)生出這樣順序的代碼:

執(zhí)行“new Widget”。
執(zhí)行computePriority梢薪。
執(zhí)行std::shared_ptr的構(gòu)造函數(shù)蹬铺。

如果這樣的代碼被產(chǎn)生出來(lái),并且在運(yùn)行期秉撇,computePriority產(chǎn)生了一個(gè)異常甜攀,則在第一步動(dòng)態(tài)分配的Widget就會(huì)泄露了,因?yàn)樗肋h(yuǎn)不會(huì)被存放到在第三步才開始管理它的std::shared_ptr中琐馆。

使用std::make_shared可以避免這樣的問(wèn)題赴邻。調(diào)用代碼將看起來(lái)像這樣:

processWidget(std::make_shared<Widget>(),       //沒有資源泄露
              computePriority());           

在運(yùn)行期,不管std::make_shared或computePriority哪一個(gè)先被調(diào)用啡捶。如果std::make_shared先被調(diào)用姥敛,則在computePriority調(diào)用前,指向動(dòng)態(tài)分配出來(lái)的Widget的原始指針能安全地被存放到被返回的std::shared_ptr中瞎暑。如果computePriority之后產(chǎn)生一個(gè)異常彤敛,std::shared_ptr的析構(gòu)函數(shù)將發(fā)現(xiàn)它持有的Widget需要被銷毀。并且如果computePriority先被調(diào)用并產(chǎn)生一個(gè)異常了赌,std::make_shared就不會(huì)被調(diào)用墨榄,因此這里就不需要考慮動(dòng)態(tài)分配的Widget了。

如果使用std::unique_ptr和std::make_unique來(lái)替換std::shared_ptr和std::make_shared勿她,事實(shí)上袄秩,會(huì)用到同樣的理由。因此逢并,使用std::make_unique代替new就和“使用std::make_shared來(lái)寫出異常安全的代碼”一樣重要之剧。

缺點(diǎn)

構(gòu)造函數(shù)是保護(hù)或私有時(shí),無(wú)法使用 make_shared

make_shared 雖好, 但也存在一些問(wèn)題, 比如, 當(dāng)我想要?jiǎng)?chuàng)建的對(duì)象沒有公有的構(gòu)造函數(shù)時(shí), make_shared 就無(wú)法使用了, 當(dāng)然我們可以使用一些小技巧來(lái)解決這個(gè)問(wèn)題, 比如這里 How do I call ::std::make_shared on a class with only protected or private constructors?

對(duì)象的內(nèi)存可能無(wú)法及時(shí)回收

make_shared 只分配一次內(nèi)存, 這看起來(lái)很好. 減少了內(nèi)存分配的開銷. 問(wèn)題來(lái)了, weak_ptr 會(huì)保持控制塊(強(qiáng)引用, 以及弱引用的信息)的生命周期, 而因此連帶著保持了對(duì)象分配的內(nèi)存, 只有最后一個(gè) weak_ptr 離開作用域時(shí), 內(nèi)存才會(huì)被釋放. 原本強(qiáng)引用減為 0 時(shí)就可以釋放的內(nèi)存, 現(xiàn)在變?yōu)榱藦?qiáng)引用, 若引用都減為 0 時(shí)才能釋放, 意外的延遲了內(nèi)存釋放的時(shí)間. 這對(duì)于內(nèi)存要求高的場(chǎng)景來(lái)說(shuō), 是一個(gè)需要注意的問(wèn)題.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砍聊,隨后出現(xiàn)的幾起案子背稼,更是在濱河造成了極大的恐慌,老刑警劉巖玻蝌,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟹肘,死亡現(xiàn)場(chǎng)離奇詭異词疼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)帘腹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門贰盗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人阳欲,你說(shuō)我怎么就攤上這事舵盈。” “怎么了胸完?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵书释,是天一觀的道長(zhǎng)翘贮。 經(jīng)常有香客問(wèn)我赊窥,道長(zhǎng),這世上最難降的妖魔是什么狸页? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任锨能,我火速辦了婚禮,結(jié)果婚禮上芍耘,老公的妹妹穿的比我還像新娘址遇。我一直安慰自己,他們只是感情好斋竞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布倔约。 她就那樣靜靜地躺著,像睡著了一般坝初。 火紅的嫁衣襯著肌膚如雪浸剩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天鳄袍,我揣著相機(jī)與錄音绢要,去河邊找鬼。 笑死拗小,一個(gè)胖子當(dāng)著我的面吹牛重罪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哀九,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剿配,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了阅束?” 一聲冷哼從身側(cè)響起惨篱,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎围俘,沒想到半個(gè)月后砸讳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琢融,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年簿寂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漾抬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡常遂,死狀恐怖纳令,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情克胳,我是刑警寧澤平绩,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站漠另,受9級(jí)特大地震影響捏雌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笆搓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一性湿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧满败,春花似錦肤频、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至净嘀,卻和暖如春报咳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背面粮。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工少孝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人熬苍。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓稍走,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親柴底。 傳聞我的和親對(duì)象是個(gè)殘疾皇子婿脸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355