langShift

智能指標

智能指標是行為類似原始指標但提供自動記憶體管理的物件,顯著降低了記憶體洩漏和懸空指標的風險。它們是現代 C++ 程式設計的基石,體現了 RAII (資源取得即初始化) 原則。本模組將探討三種主要類型的智能指標 (unique_ptrshared_ptrweak_ptr),並將它們的記憶體管理方法與 JavaScript 的垃圾回收進行比較。

unique_ptr 獨占所有權

std::unique_ptr 是一種智能指標,它獨占其指向的物件。這意味著在任何時候,只有一個 unique_ptr 可以指向給定物件。當 unique_ptr 超出作用域時,它擁有的物件會自動刪除。

  • 獨占所有權: 不能複製,只能移動。
  • 輕量級: 開銷極小,類似於原始指標。
  • 用例: 當你需要動態分配物件的單一所有者時。
正在加载...

shared_ptr 共享所有權

std::shared_ptr 是一種智能指標,允許多個 shared_ptr 實例擁有同一個物件。它使用引用計數來跟蹤有多少個 shared_ptr 指向該物件。只有當最後一個擁有它的 shared_ptr 被銷毀或重置時,物件才會被刪除。

  • 共享所有權: 可以複製。
  • 引用計數: 根據所有者數量管理物件生命週期。
  • 用例: 當程式碼的多個部分需要共享物件的所有權時。
正在加载...

weak_ptr 弱引用

std::weak_ptr 是一種非擁有智能指標。它持有對由 shared_ptr 管理的物件的「弱」引用,而不增加引用計數。這主要用於打破循環引用,否則會阻止物件被刪除。

  • 非擁有: 不影響物件的生命週期。
  • 用例: 打破 shared_ptr 之間的循環依賴。
  • 存取: 在存取受管理物件之前,必須使用 lock() 將其轉換為 shared_ptr。如果物件已被刪除,lock() 返回一個空 shared_ptr

智能指標 vs. 原始指標

特性原始指標智能指標 (unique_ptrshared_ptr)
記憶體管理手動 (new/delete)自動 (RAII)
所有權無明確所有權語義明確所有權語義
安全性容易出現記憶體洩漏、懸空指標、重複釋放顯著減少記憶體錯誤
開銷極小少量開銷 (例如,shared_ptr 的引用計數)
用例低階記憶體存取,C 風格 API現代 C++ 記憶體管理

經驗法則: 除非有充分理由(例如,與 C API 交互),否則優先使用智能指標而不是原始指標來管理動態分配的記憶體。

循環引用問題

當兩個或多個 shared_ptr 相互持有引用,形成一個循環時,就會發生循環引用。這會阻止它們的引用計數達到零,從而導致記憶體洩漏。

正在加载...

智能指標最佳實踐

  1. 預設優先使用 unique_ptr 如果你需要獨占所有權,unique_ptr 是最有效和最安全的選擇。
  2. 共享所有權時使用 shared_ptr 當多個實體需要共享物件的所有權時,shared_ptr 是合適的。
  3. 使用 weak_ptr 打破循環: 始終使用 weak_ptr 來解決 shared_ptr 之間的循環引用。
  4. 使用 std::make_uniquestd::make_shared 這些工廠函式優於直接 new 來建立智能指標。它們提供異常安全,並且可能更高效。
  5. 避免 get() 和原始指標: 僅在絕對必要時(例如,與 C API 交互)才使用 get() 獲取原始指標,並極其小心其生命週期。
  6. 不要將原始 new/delete 與智能指標混用: 一旦物件由智能指標管理,就讓智能指標處理其生命週期。

與 JavaScript 垃圾回收的比較

JavaScript 依賴垃圾回收 (GC) 進行自動記憶體管理。GC 定期識別並回收不再被程式可達的記憶體。這種方法簡化了開發人員的記憶體管理,因為他們不需要明確地分配或釋放記憶體。

特性JavaScript (GC)C++ (智能指標)
機制自動,運行時 (標記-清除、引用計數等)自動,編譯時/運行時 (RAII,shared_ptr 的引用計數)
控制對記憶體的直接控制較少物件生命週期的精細控制
確定性不確定性 (GC 在不可預測的時間運行)確定性 (智能指標超出作用域時物件被銷毀)
性能GC 暫停可能引入延遲可預測的性能,開銷極小
循環引用由現代 GC 處理 (例如,標記-清除)需要 weak_ptr 打破循環

雖然 JavaScript 的 GC 提供了便利,但 C++ 智能指標提供了確定性銷毀和精細控制,這對於性能關鍵應用程式和需要立即釋放資源(例如,檔案句柄、網路連線)的資源管理至關重要。


練習題:

  1. 解釋 std::unique_ptr 實現的獨占所有權概念。何時會選擇 unique_ptr 而不是 shared_ptr
  2. std::shared_ptr 如何管理物件生命週期?描述一個需要 std::weak_ptr 來防止記憶體洩漏的場景。
  3. 比較和對比 C++ 智能指標與 JavaScript 的垃圾回收。討論每種方法的優缺點。

專案構想:

  • 使用 std::shared_ptr 實現一個簡單的圖資料結構(節點和邊)。引入一個可能發生循環引用的場景(例如,兩個節點之間的雙向邊)。然後,修改你的實現以使用 std::weak_ptr 來打破循環引用並確保正確的記憶體釋放。