智慧指標讓介面中的所有權清楚可見。但它們不會取代僅用於借用物件的參照與原始指標;對於這類物件,其生命週期由其他位置保證。應根據生命週期契約選擇指標型別,並在 API 邊界明確表達所有權轉移與物件觀察。
唯一所有權 #
std::unique_ptr<T> 擁有一個物件,且只能移動。普通的 new 與預設刪除器適用時,應使用 std::make_unique 建立;若要轉移所有權,應以值傳遞或以值回傳。
#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 可以同時配置物件與控制區塊,從而減少配置次數。由於弱觀察者會保留控制區塊,這種合併配置可能導致物件的儲存空間直到觀察者消失後才可釋放。
別名建構函式在儲存不同指標的同時共用既有的控制區塊:
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 契約 #
使用能夠說明預期關係的簽章:
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 上閱讀或評論(發表評論需要登入)