C++ 智慧指標與所有權

最後更新: 2024-02-28 10:22:21 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

智慧指標讓介面中的所有權清楚可見。但它們不會取代僅用於借用物件的參照與原始指標;對於這類物件,其生命週期由其他位置保證。應根據生命週期契約選擇指標型別,並在 API 邊界明確表達所有權轉移與物件觀察。

唯一所有權 #

std::unique_ptr<T> 擁有一個物件,且只能移動。普通的 new 與預設刪除器適用時,應使用 std::make_unique 建立;若要轉移所有權,應以值傳遞或以值回傳。

cpp
#include <memory>

std::unique_ptr<Connection> open_connection(Address address);
void install_connection(std::unique_ptr<Connection> connection);

install_connection(open_connection(address));

自訂刪除器是 unique_ptr 型別的一部分,因此可能影響其大小以及 API 接受的型別。刪除器必須符合所有權協定,且不得在解構過程中擲出例外。

即使 T 是不完整型別,unique_ptr<T> 也可以出現在標頭檔中。不過,預設刪除器要求在實例化銷毀物件的操作處,T 已是完整型別;這通常是擁有該物件之類別的類別外解構函式。unique_ptr<T[]> 表示陣列配置,但不會保留陣列長度;對於擁有的序列,應優先使用 std::vector<T>。

共用所有權與觀察 #

std::shared_ptr<T> 共用一個控制區塊,其中包含所有權狀態,通常也包含刪除器與配置器。複製 shared_ptr 會延長所有權;最後一個擁有物件的指標會銷毀受管理物件。std::make_shared 可以同時配置物件與控制區塊,從而減少配置次數。由於弱觀察者會保留控制區塊,這種合併配置可能導致物件的儲存空間直到觀察者消失後才可釋放。

別名建構函式在儲存不同指標的同時共用既有的控制區塊:

cpp
auto owner = std::make_shared<Record>();
std::shared_ptr<Member> member(owner, &owner->member);

member.get() 指向子物件,但其生命週期由 owner 所擁有的 Record 控制。不要從同一個原始指標建構彼此無關的 shared_ptr 實例;每個實例都會認為自己獨立擁有該物件。

std::weak_ptr<T> 會觀察控制區塊,但不會延長物件的生命週期。解參照前,應使用 lock() 取得暫時的擁有指標。當所有權循環會使物件圖無限期存活時,應使用 weak_ptr 對反向邊與快取進行建模。

API 契約 #

使用能夠說明預期關係的簽章:

cpp
void take(std::unique_ptr<Job>);             // 轉移唯一所有權
void share(std::shared_ptr<const Config>);    // 保留共用所有權
void inspect(const Config&);                  // 借用;非空
void maybe_inspect(const Config*);            // 借用;可為空

僅當操作既不需要保留所有權,也不需要公開所有權契約時,才接受 const std::shared_ptr<T>&;接受 T& 或 T* 能更直接地記錄借用關係。借用的指標或參照僅在呼叫端承諾的生命週期內有效。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)