std::make_shared有啥用
http://bitdewy.github.io/blog/2014/01/12/why-make-shared/
C++11 中引入了智慧指標, 同時還有一個模板函式 std::make_shared
可以返回一個指定型別的 std::shared_ptr
,
那與std::shared_ptr
的建構函式相比它能給我們帶來什麼好處呢
?
優點
效率更高
shared_ptr
需要維護引用計數的資訊,
- 強引用, 用來記錄當前有多少個存活的 shared_ptrs 正持有該物件. 共享的物件會在最後一個強引用離開的時候銷燬( 也可能釋放).
- 弱引用, 用來記錄當前有多少個正在觀察該物件的 weak_ptrs. 當最後一個弱引用離開的時候, 共享的內部資訊控制塊會被銷燬和釋放 (共享的物件也會被釋放, 如果還沒有釋放的話).
如果你通過使用原始的 new 表示式分配物件, 然後傳遞給 shared_ptr (也就是使用 shared_ptr 的建構函式) 的話, shared_ptr 的實現沒有辦法選擇, 而只能單獨的分配控制塊:
1 2 |
|
如果選擇使用 make_shared
的話, 情況就會變成下面這樣:
1
|
|
記憶體分配的動作, 可以一次性完成. 這減少了記憶體分配的次數, 而記憶體分配是代價很高的操作.
異常安全
看看下面的程式碼:
1 2 3 4 |
|
C++ 是不保證引數求值順序, 以及內部表示式的求值順序的, 所以可能的執行順序如下:
- new Lhs(“foo”))
- new Rhs(“bar”))
- std::shared_ptr
- std::shared_ptr
好了, 現在我們假設在第 2 步的時候, 丟擲了一個異常 (比如 out of memory, 總之, Rhs 的建構函式異常了), 那麼第一步申請的 Lhs 物件記憶體洩露了. 這個問題的核心在於, shared_ptr 沒有立即獲得裸指標.
我們可以用如下方式來修復這個問題.
1 2 3 |
|
當然, 推薦的做法是使用 std::make_shared
來代替:
1
|
|
缺點
建構函式是保護或私有時,無法使用 make_shared
物件的記憶體可能無法及時回收
make_shared
只分配一次記憶體, 這看起來很好. 減少了記憶體分配的開銷.
問題來了, weak_ptr
會保持控制塊(強引用, 以及弱引用的資訊)的生命週期,
而因此連帶著保持了物件分配的記憶體, 只有最後一個 weak_ptr
離開作用域時,
記憶體才會被釋放. 原本強引用減為 0 時就可以釋放的記憶體, 現在變為了強引用, 若引用都減為 0 時才能釋放, 意外的延遲了記憶體釋放的時間. 這對於記憶體要求高的場景來說, 是一個需要注意的問題. 關於這個問題可以看這裡 make_shared,
almost a silver bullet