1. 程式人生 > >智慧指標boost::weak_ptr 詳解

智慧指標boost::weak_ptr 詳解

1、boost::weak_ptr簡介
boost::weak_ptr屬於boost庫,定義在namespace boost中,包含標頭檔案 #include<boost/weak_ptr.hpp>便可以使用。
2、boost::weak_ptr詳解
智慧指標boost::scope_ptr和智慧指標boost::shared_ptr就完全可以解決所有單個物件記憶體的管理問題。
這兒咋還多出一個boost::weak_ptr,是否還有某些案例我們沒有考慮到呢?
回答:有。首先 boost::weak_ptr是專門為boost::shared_ptr而準備的。
有時候,我們只關心能否使用物件,並不關心內部的引用計數。
boost::weak_ptr是 boost::shared_ptr的觀察者(Observer)物件,觀察者?觀察者怎麼理解呢?
觀察者意味著boost::weak_ptr只對boost::shared_ptr進行引用,而不改變其引用計數。
當被觀察的boost::shared_ptr失效後,相應的boost::weak_ptr也隨之失效。
那麼,為什麼需要這個觀察者呢?那還得從迴圈引用談起。
引用計數是一種便利的記憶體管理機制,但它有一個很大的缺點,那就是不能管理迴圈引用的物件。
示例程式碼如下:

 1 #include <iostream>
 2 #include <boost/shared_ptr.hpp>
 3 #include <boost/weak_ptr.hpp>
 4 
 5 class parent;
 6 class children;
 7 
 8 typedef boost::shared_ptr<parent> parent_ptr;
 9 typedef boost::shared_ptr<children> children_ptr;
10 
11 class parent
12 {
13 public
: 14 ~parent() { std::cout <<"destroying parent\n"; } 15 16 public: 17 children_ptr children; 18 }; 19 20 class children 21 { 22 public: 23 ~children() { std::cout <<"destroying children\n"; } 24 25 public: 26 parent_ptr parent; 27 }; 28 29 30 void test() 31 { 32 parent_ptr father(new
parent()); 33 children_ptr son(new children); 34 35 father->children = son; 36 son->parent = father; 37 } 38 39 void main() 40 { 41 std::cout<<"begin test...\n"; 42 test(); 43 std::cout<<"end test.\n"; 44 }

複製程式碼
執行該程式可以看到:即使退出了test函式後,由於parent和children物件互相引用,它們的引用計數都是1,不能自動釋放,並且此時這兩個物件再無法訪問到。
這也就引起了C++中那臭名昭著的記憶體洩漏。
<1>一般來講,解除這種迴圈引用有下面有三種可行的方法:
1. 當只剩下最後一個引用的時候需要手動打破迴圈引用釋放物件。
2. 當parent的生存期超過children的生存期的時候,children改為使用一個普通指標指向parent。
3. 使用弱引用的智慧指標打破這種迴圈引用。
雖然這三種方法都可行,但方法1和方法2都需要程式設計師手動控制,麻煩且容易出錯。
下面主要介紹一下第三種方法和boost中的弱引用的智慧指標boost::weak_ptr。
<2>什麼是強引用和弱引用?
一個強引用是指當被引用的物件仍活著的話,這個引用也存在(也就是說,只要至少有一個強引用,那麼這個物件就不會也不能被釋放)。boost::share_ptr就是強引用。
相對而言,弱引用當引用的物件活著的時候不一定存在。僅僅是當它自身存在的時的一個引用。
弱引用並不修改該物件的引用計數,這意味這弱引用它並不對物件的記憶體進行管理。
在功能上類似於普通指標,然而一個比較大的區別是,弱引用能檢測到所管理的物件是否已經被釋放,從而避免訪問非法記憶體。
boost::weak_ptr boost::weak_ptr是boost提供的一個弱引用的智慧指標,它的宣告可以簡化如下:

 1 namespace boost 
 2 {
 3 template<typename T> class weak_ptr
 4 {
 5 public:
 6     template <typename Y>
 7     weak_ptr(const shared_ptr<Y>& r);
 8 
 9     weak_ptr(const weak_ptr& r);
10     
11     ~weak_ptr();
12 
13     T* get() const; 
14     bool expired() const; 
15     shared_ptr<T> lock() const;
16 }; 
17 }

複製程式碼
可以看到,boost::weak_ptr必須從一個boost::share_ptr或另一個boost::weak_ptr轉換而來,這也說明,進行該物件的記憶體管理的是那個強引用的boost::share_ptr。
boost::weak_ptr只是提供了對管理物件的一個訪問手段。
boost::weak_ptr除了對所管理物件的基本訪問功能(通過get()函式)外,還有兩個常用的功能函式:
1. expired() 用於檢測所管理的物件是否已經釋放;
2. lock() 用於獲取所管理的物件的強引用指標。
<3>通過boost::weak_ptr來打破迴圈引用
由於弱引用不更改引用計數,類似普通指標,只要把迴圈引用的一方使用弱引用,即可解除迴圈引用。
對於上面的那個例子來說,只要把children的定義改為如下方式,即可解除迴圈引用:

1 class children
2 {
3 public:
4     ~children() { std::cout <<"destroying children\n"; }
5 
6 public:
7     boost::weak_ptr<parent> parent;
8 };

【3】boost::weak_ptr總結
雖然通過弱引用指標可以有效的解除迴圈引用,但這種方式必須在程式設計師能預見會出現迴圈引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案。
如果程式在執行過程中出現了迴圈引用,還是會造成記憶體洩漏的。
因此,不要認為只要使用了智慧指標便能杜絕記憶體洩漏。
畢竟,對於C++來說,由於沒有垃圾回收機制,記憶體洩漏對每一個程式設計師來說都是一個非常頭痛的問題。