天天看點

C++11使用make_shared的優勢和劣勢

C++11 中引入了智能指針, 同時還有一個模闆函數 <code>std::make_shared</code> 可以傳回一個指定類型的 <code>std::shared_ptr</code>, 那與

<code>std::shared_ptr</code> 的構造函數相比它能給我們帶來什麼好處呢 ?

<code>shared_ptr</code> 需要維護引用計數的資訊,

強引用, 用來記錄目前有多少個存活的 shared_ptrs 正持有該對象. 共享的對象會在最後一個強引用離開的時候銷毀( 也可能釋放).

弱引用, 用來記錄目前有多少個正在觀察該對象的 weak_ptrs. 當最後一個弱引用離開的時候, 共享的内部資訊控制塊會被銷毀和釋放 (共享的對象也會被釋放, 如果還沒有釋放的話).

如果你通過使用原始的 new 表達式配置設定對象, 然後傳遞給 shared_ptr (也就是使用 shared_ptr 的構造函數) 的話, shared_ptr 的實作沒有辦法選擇, 而隻能單獨的配置設定控制塊:

C++11使用make_shared的優勢和劣勢

如果選擇使用 <code>make_shared</code> 的話, 情況就會變成下面這樣:

C++11使用make_shared的優勢和劣勢

記憶體配置設定的動作, 可以一次性完成. 這減少了記憶體配置設定的次數, 而記憶體配置設定是代價很高的操作.

看看下面的代碼:

C++ 是不保證參數求值順序, 以及内部表達式的求值順序的, 是以可能的執行順序如下:

new Lhs(“foo”))

new Rhs(“bar”))

std::shared_ptr

好了, 現在我們假設在第 2 步的時候, 抛出了一個異常 (比如 out of memory, 總之, Rhs 的構造函數異常了), 那麼第一步申請的 Lhs 對象記憶體洩露了. 這個問題的核心在于, shared_ptr 沒有立即獲得裸指針.

我們可以用如下方式來修複這個問題.

當然, 推薦的做法是使用 <code>std::make_shared</code> 來代替:

<code>make_shared</code> 雖好, 但也存在一些問題, 比如, 當我想要建立的對象沒有公有的構造函數時, <code>make_shared</code> 就無法使用了, 當然我們可以使用一些小技巧來解決這個問題, 比如這裡

<a href="http://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const?rq=1">How do I call ::std::make_shared on a class with only protected or private constructors?</a>

<code>make_shared</code> 隻配置設定一次記憶體, 這看起來很好. 減少了記憶體配置設定的開銷. 問題來了, <code>weak_ptr</code> 會保持控制塊(強引用, 以及弱引用的資訊)的生命周期, 而是以連帶着保持了對象配置設定的記憶體, 隻有最後一個

<code>weak_ptr</code> 離開作用域時, 記憶體才會被釋放. 原本強引用減為 0 時就可以釋放的記憶體, 現在變為了強引用, 若引用都減為 0 時才能釋放, 意外的延遲了記憶體釋放的時間. 這對于記憶體要求高的場景來說, 是一個需要注意的問題. 關于這個問題可以看這裡

<a href="http://lanzkron.wordpress.com/2012/04/22/make_shared-almost-a-silver-bullet/">make_shared, almost a silver bullet</a>

<a href="http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/">GotW #89 Solution: Smart Pointers</a>

<a href="http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared">cppreference.com – std::make_shared</a>

繼續閱讀