首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective Modern C++ 條款 21:優先使用 make_unique / make_shared 而非直接 new

     編輯:關於C++

現代 C++ 強烈建議使用 std::make_unique(C++14)和 std::make_shared(C++11)創建智能指針,而非手動 new 再包裝。本條款從代碼簡潔性、異常安全、性能優化三個維度論證優勢,並闡述不適用場景與替代方案。

一、為什麼優先用 make 函數?

1. 消除代碼重複(DRY 原則)

// 使用 new:類型 Widget 出現兩次
std::unique_ptr<Widget> upw1(new Widget);
std::shared_ptr<Widget> spw1(new Widget);

// 使用 make:類型僅出現一次
auto upw2 = std::make_unique<Widget>();
auto spw2 = std::make_shared<Widget>();

減少重複降低維護成本、編譯時間與目標代碼膨脹。

2. 異常安全

考慮函數調用 processWidget(std::shared_ptr<Widget>, computePriority()),參數求值順序未定序。若先 new Widget 再 computePriority() 拋異常,裸指針洩漏。make_shared 將分配與構造融合為單一表達式,無中間裸指針狀態,天然異常安全。

// 危險:可能洩漏
processWidget(std::shared_ptr<Widget>(new Widget), computePriority());

// 安全:make_shared 保證無洩漏
processWidget(std::make_shared<Widget>(), computePriority());

make_unique 同理。

3. 性能優勢(make_shared 專屬)

  • 單次分配:make_shared 將對象與控制塊放在同一塊內存,僅需一次堆分配;new + shared_ptr 需兩次(對象 + 控制塊)。
  • 代碼體積更小:僅調用一次分配函數。
  • 局部性更好:對象與引用計數相鄰,缓存友好。

std::allocate_shared 同享此優勢。

二、不適用場景與替代方案

場景原因替代方案
需要自定義刪除器 make 函數不支持指定刪除器 直接 new 構造智能指針:
std::shared_ptr<Widget> spw(new Widget, deleter);
需要大括號初始化列表構造 完美轉發不支持大括號列表(見條款 30) 先用 auto init = {1,2}; 推導為 initializer_list 再傳遞,或直接 new
類自定義 operator new/delete make_shared 分配塊包含控制塊,大小不等於 sizeof(T),破壞自定義分配器假設 直接 new 或 allocate_shared 配合兼容分配器
大對象 + 長壽 weak_ptr 控制塊與對象同塊內存,weak_ptr 存活導致大對象內存延遲釋放 直接 new,對象與控制塊分離分配

三、無法使用 make 時的異常安全手動寫法

將 new 與智能指針構造放在單獨語句,避免編譯器在兩者間插入其他求值:

// 需自定義刪除器
std::shared_ptr<Widget> spw(new Widget, customDeleter); // 單一語句
processWidget(std::move(spw), computePriority());      // 移動語義避免拷貝引用計數開銷
  • 單語句保證 new 與構造原子性,異常時刪除器會被調用。
  • 用 std::move 傳遞右值,避免 shared_ptr 拷貝的原子增減開銷。

四、C++11 手寫 make_unique

標準庫 C++14 才提供,C++11 可自行實現簡易版:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// 注意:勿放入 std 命名空間,避免升級 C++14 時衝突

五、總結:必記三點

  1. make_unique/make_shared 消除代碼重複、提升異常安全、make_shared 更有性能優勢。
  2. 不適用場景:自定義刪除器、大括號初始化、自定義 operator new/delete 的類、大對象配合長壽 weak_ptr。
  3. 若必須手動 new,請在單獨語句中完成分配與構造,並用 std::move 傳遞右值以保持效率。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved