# weak_ptr
弱指針(weak pointer)經常被解釋為用來打破使用shared_ptr管理的數據結構中循環(?)。但是我認為,將weak_ptr看成是指向具有下列特征的對象的指針更好一些。
* 只有當對象存在的時候,你才需要對其進行訪問
* 并且它可能被其他人刪除釋放
* 并且在最后一次使用之后調用其析構函數(通常用于釋放那些不具名的內存(anon-memory)資源
(譯注:weak_ptr可以保存一個“弱引用”,指向一個已經用shared_ptr進行管理的對象。為了訪問這個對象,一個weak_ptr可以通過shared_ptr的構造函數或者是weak_ptr的成員函數lock()轉化為一個shared_ptr。當最后一個指向這個對象的shared_ptr退出其生命周期并且這個對象被釋放之后,將無法從指向這個對象的weak_ptr獲得一個shared_ptr指針,shared_ptr的構造函數會拋出異常,而weak_ptr::lock也會返回一個空指針。)
我們來考慮一下如何實現一個老式的“星盤棋”( asteroid game)游戲,所有星星(asteroid)都屬于游戲(the game),但是所有星星都必須與它周圍的 星星保持聯系,并且與之保持相反的狀態。要維持一個相反的狀態,通常會消去一個或者多個星星,也就是會調用其析構函數。每個星星必須有一個列表來保存記錄它周圍的星星。這里需要注意的是,在這樣一個相鄰星星列表中的星星不應該是具有完整生命的(?),所以shared_ptr在這種情況下并不適合。另外一方面,當另外一個星星正看著某個星星時,(例如,依賴于這個星星計算其相反狀態),這個星星就不能被析構。 當然,星星的析構函數必須被調用以釋放其占用的資源(比如與圖形系統的連接)。我們所需要的是一個在任何時間都應該保持完整無缺,并且隨時都可以從中獲取一個星星的星星列表(?)。weak_ptr可以幫我們做到這一切:
```
void owner()
{
// …
vector<shared_ptr<Asteroid>> va(100);
for (int i=0; i<va.size(); ++i) { // 訪問相鄰的星星,計算相反狀態
va[i].reset(new Asteroid(weak_ptr(va[neighbor]));
launch(i);
}
// …
}
```
reset() 可以讓一個shared_ptr指向另外一個新的對象。
當然,我對ower類作了相當大的簡化,并且只給了每個星星一個鄰居。這里的關鍵是,我們使用了weak_ptr指向其鄰居星星。在計算相反狀態的時候,ower類則使用shared_ptr來代表星星與owner之間的所屬關系(?)。一個星星的相反關系的計算應該是這樣的:
```
void collision(weak_ptr<Asteroid> p)
{
// p.lock返回一個指向p所指對象的shared_ptr
if (auto q = p.lock()) {
// … p以及q指向的星星對象依然存在:進行計算…
}
else {
// … oops: 星星對象已經被析構,我們可以忘掉它了(?)…
}
}
```
注意,即使owner決定關閉整個游戲并釋放所有的星星對象(通過刪除代表所屬關系的多個shared_ptr),每一個正在計算過程中的星星對象仍然可以正確地結束,因為p.lock()將維持一個shared_ptr,直到計算過程結束。(譯注,也即是說,如果正在計算過程中關閉游戲并通過shared_ptr釋放對象,那么p.lock()會維持一個shared_ptr,這樣可以使得shared_ptr不會變成0,在計算過程中,星星對象也就不會被錯誤地釋放。當整個計算過程結束后,shared_ptr的引用計數變為0,星星對象被正確釋放。)
我期望看到weak_ptr比簡單的shared_ptr更少地被用到,并且我希望unique_ptr可以比shared_ptr更加流行,因為unique_ptr的所屬關系更簡單一些(譯注:只能有一個 unique_ptr指針指向某個對象,不向shared_ptr,可以同時有多個shared_ptr指向同一個對象)并且性能更高,因而可以讓局部的代碼更容易理解。
參考
* the C++ draft: Weak_ptr (20.7.13.3)
- C++11 FAQ中文版 - C++11 FAQ
- Stroustrup先生關于中文版的授權許可郵件
- Stroustrup先生關于C++11 FAQ的一些說明
- 關于C++11的一般性的問題
- 您是如何看待C++11的?
- 什么時候C++0x會成為一部正式的標準呢?
- 編譯器何時將會實現C++11標準呢?
- 我們何時可以用到新的標準庫文件?
- C++0x將提供何種新的語言特性呢?
- C++11會提供哪些新的標準庫文件呢?
- C++0x努力要達到的目標有哪些?
- 指導標準委員會的具體設計目標是什么?
- 在哪里可以找到標準委員會的報告?
- 從哪里可以獲得有關C++11的學術性和技術性的參考資料?
- 還有哪些地方我可以讀到關于 C++0x的資料?
- 有關于C++11的視頻嗎?
- C++0x難學嗎?
- 標準委員會是如何運行的?
- 誰在標準委員會里?
- 實現者應以什么順序提供C++11特性?
- 將會是C++1x嗎?
- 標準中的"concepts"怎么了?
- 有你不喜歡的C++特性嗎?
- 關于獨立的語言特性的問題
- __cplusplus宏
- alignment(對齊方式)
- 屬性(Attributes)
- atomic_operations
- auto – 從初始化中推斷數據類型
- C99功能特性
- 枚舉類——具有類域和強類型的枚舉
- carries_dependency
- 復制和重新拋出異常
- 常量表達式(constexpr)
- decltype – 推斷表達式的數據類型
- 控制默認函數——默認或者禁用
- 控制默認函數——移動(move)或者復制(copy)
- 委托構造函數(Delegating constructors)
- 并發性動態初始化和析構
- noexcept – 阻止異常的傳播與擴散
- 顯式轉換操作符
- 擴展整型
- 外部模板聲明
- 序列for循環語句
- 返回值類型后置語法
- 類成員的內部初始化
- 繼承的構造函數
- 初始化列表
- 內聯命名空間
- Lambda表達式
- 用作模板參數的局部類型
- long long(長長整數類型)
- 內存模型
- 預防窄轉換
- nullptr——空指針標識
- 對重載(override)的控制: override
- 對重載(override)的控制:final
- POD
- 原生字符串標識
- 右角括號
- 右值引用
- Simple SFINAE rule
- 靜態(編譯期)斷言 — static_assert
- 模板別名(正式的名稱為"template typedef")
- 線程本地化存儲 (thread_local)
- unicode字符
- 統一初始化的語法和語義
- (廣義的)聯合體
- 用戶定義數據標識(User-defined literals)
- 可變參數模板(Variadic Templates)
- 關于標準庫的問題
- abandoning_a_process
- 算法方面的改進
- array
- async()
- atomic_operations
- 條件變量(Condition variables)
- 標準庫中容器方面的改進
- std::function 和 std::bind
- std::forward_list
- std::future和std::promise
- 垃圾回收(應用程序二進制接口)
- 無序容器(unordered containers)
- 鎖(locks)
- metaprogramming(元編程)and type traits
- 互斥
- 隨機數的產生
- 正則表達式(regular expressions)
- 具有作用域的內存分配器
- 共享資源的智能指針——shared_ptr
- smart pointers
- 線程(thread)
- 時間工具程序
- 標準庫中的元組(std::tuple)
- unique_ptr
- weak_ptr
- system error