簡介
boost::shared_ptr是可以共享所有權的指針。如果有多個shared_ptr共同管理同一個對象時,只有這些shared_ptr全部與該對象脫離關系之后,被管理的對象才會被釋放。通過下面這個例子先了解下shared_ptr的基本用法:
1 #include <iostream> 2 #include <string> 3 #include <boost/shared_ptr.hpp> 4 5 using namespace std; 6 7 class Book 8 { 9 private: 10 string name_; 11 12 public: 13 Book(string name) : name_(name) 14 { 15 cout << "Creating book " << name_ << " ..." << endl; 16 } 17 18 ~Book() 19 { 20 cout << "Destroying book " << name_ << " ..." << endl; 21 } 22 }; 23 24 int main() 25 { 26 cout << "=====Main Begin=====" << endl; 27 { 28 boost::shared_ptr<Book> myBook(new Book("「1984」")); 29 cout << "[From myBook] The ref count of book is " << myBook.use_count() << ".\n" << endl; 30 31 boost::shared_ptr<Book> myBook1(myBook); 32 cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl; 33 cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << ".\n" << endl; 34 35 cout << "Reset for 1th time. Begin..." << endl; 36 myBook.reset(); 37 cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl; 38 cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << "." << endl; 39 cout << "Reset for 1th time. End ...\n" << endl; 40 41 cout << "Reset for 2th time. Begin ..." << endl; 42 myBook1.reset(); 43 cout << "Reset for 2th time. End ..." << endl; 44 } 45 cout << "===== Main End =====" << endl; 46 47 return 0; 48 }
運行結果:

運行過程分析:
shared_ptr的管理機制其實並不復雜,就是對所管理的對象進行了引用計數,當新增一個shared_ptr對該對象進行管理時,就將該對象的引用計數加一;減少一個shared_ptr對該對象進行管理時,就將該對象的引用計數減一,如果該對象的引用計數為0的時候,說明沒有任何指針對其管理,才調用delete釋放其所占的內存。
1) 創建Book對象,將其分配給myBook管理,此時其使用計數為1。
2) 將myBook的所有權共享給myBook1,查看通過這2個shared_ptr查看引用計數都為2。說明當所有權共享時,通過每個shared_ptr查看到的引用計數值是一樣。
3) 剝奪myBook的所有權,通過myBook查看到的引用計數值變為0(脫離關系,變為0),通過myBook查看到的引用計數值變為1(共享者少了1個,減1)。
4) 當剝奪最后一個shared_ptr對其控制對象的所有權時,被管理的對象將被釋放。
內部實現
下面是boost::shared_ptr內部實現所涉及到的類關系圖(部分類屬性和成員函數省略):

shared_ptr<T>:px代表指向具體對象的指針;pn保存引用計數相關信息。shared_ptr的核心就是shared_count,因為整個shared_ptr實現都沒有出現對引用計數的具體操作,比如+1 -1等。而每一次需要用到對引用計數的操作都調用了shared_count內部封裝的函數,比如:swap、==、get_deleter、use_count等。
shared_count:內部包含sp_counted_base *pi_,該成員在shared_count的構造函數里初始化。
1 // 構造函數(通過被管理對象類型構造) 2 template<class Y> explicit shared_count( Y * p ): pi_( 0 ) 3 { 4 ... 5 pi_ = new sp_counted_impl_p<Y>( p ); 6 ... 7 } 8 9 // 拷貝構造函數 10 shared_count(shared_count const & r): pi_(r.pi_) // nothrow 11 { 12 if( pi_ != 0 ) pi_->add_ref_copy(); 13 }
sp_count_base:該類主要對引用計數進行管理,used_count是被引用的此時;weak_count涉及到weak_ptr的相關內容,這里就先不討論。add_ref_copy()內部會對used_count進行自加。release()內部調用dispose(),不過dispose()需要派生類自己實現。創建派生類對象時,會先初始化sp_count_base類對象中的use_count為1。
下面我們簡化下場景:創建一個名為myBook的shared_ptr對象來管理一個Book對象,再通過myBook新建名為myBook1的shared_ptr對象。通過如下時序圖來回顧下整個流程:

總結
與裸指針相比,shared_ptr 會有一點點額外的空間代價。我還沒有發現由於這些代價太大而需要另外尋找一個解決方案的情形。不要去創建你自己的引用計數智能指針類。沒有比使用 shared_ptr 智能指針更好的了。和前面介紹的boost::scoped_ptr相比,boost::shared_ptr可以共享對象的所有權,因此其使用范圍基本上沒有什么限制,自然也可以使用在stl的容器中。另外它還是線程安全的,但boost::shared_ptr並不是絕對安全,下面幾條規則能使我們更加安全的使用boost::shared_ptr:
- 避免對shared_ptr所管理的對象的直接內存管理操作,以免造成該對象的重釋放。
- shared_ptr並不能對循環引用的對象內存自動管理(這點是其它各種引用計數管理內存方式的通病)。
- 不要構造一個臨時的shared_ptr作為函數的參數(有內存泄露風險,取決於編譯器廠商的實現)。
1 void f(shared_ptr<int>, int); 2 int g(); 3 4 void ok() 5 { 6 shared_ptr<int> p(new int(2)); 7 f(p, g()); 8 } 9 10 void bad() 11 { 12 f(shared_ptr<int>(new int(2)), g()); 13 }
關於這個問題更多相關的內容:http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/shared_ptr.htm
參考
- http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html
- http://blog.chinaunix.net/uid-21706718-id-3563640.html
- Björn Karlsson:Beyond the C++ Standard Library: An Introduction to Boost(《超越C++標准庫:Boost庫導論》)
(完)
