1. boost::shared_ptr的用法
下面用一個簡單的例子說明shared_ptr的用法:
#include <stdio.h> #include <boost/shared_ptr.hpp> class A { public: void print() { printf("class A print!\n"); } }; int main(int argc, char **argv) { boost::shared_ptr<A> a1(new A()); a1->print(); }
shared_ptr不用手動去釋放資源,它會智能地在合適的時候去自動釋放。如上面的例子,a1指向的對象將會在程序結束的時候自動釋放(程序結束時所有申請的資源都會被釋放,這只是為了說明其作用)。再來看下面的例子:
//同上 int main(int argc, char **argv) { boost::shared_ptr<A> a1(new A()); a1->print(); printf("a1 reference count: %d\n", a1.use_count()); boost::shared_ptr<A> a2 = a1; printf("a1 reference count: %d\n", a1.use_count()); printf("a2 reference count: %d\n", a2.use_count()); a1.reset(); printf("a2 reference count: %d\n", a2.use_count()); }
程序輸出結果:
class A print! a1 reference count: 1 a1 reference count: 2 a2 reference count: 2 a2 reference count: 1
上面調用了兩上shared_ptr的成員方法,user_count()的作用是獲得當前對象被引用的次數,reset()的作用是釋放指針對對象的引用,將指針設為空。
2. boost::shared_ptr的實現機制
boost::shared_ptr的實現機制其實比較簡單,就是對指針引用的對象進行引用計數,當有一個新的boost::shared_ptr指針指向一個對象時,就把該對象的引用計數加1,減少一個boost::shared_ptr指針指向一個對象時,就把對該對象的引用計數減1。當一個對象的引用計數變為0時,就會自動調用其析構函數或者free掉相應的空間。
boost::shared_ptr的常用成員函數:
(1) 構造一個空的指針
shared_ptr(); // never throws shared_ptr(std::nullptr_t); // never throws template<class D> shared_ptr(std::nullptr_t p, D d); template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
上面幾個函數可以初始化一個空的shared_ptr指針,其中,第三和第四個函數中的參數的意思是:d表示一個刪除器(deleter),它會在釋放資源的時候被調用,delete p會變成d(p)。a表示一個構造器,被用作分配空間。這兩個接口允許調用者自己提供構造器和刪除器,來自定義自己的構造和釋放行為。
(2) 根據變量構造指針
template<class Y> explicit shared_ptr(Y * p); template<class Y, class D> shared_ptr(Y * p, D d); template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
這幾個構造函數是通過一個Y類型的指針類型p來初始化shared_ptr指針,初始化后,指針會指針p所指的對象。其中,參數d和a的意義和上面相同。
(3) 拷貝構造函數
shared_ptr擁有常見的拷貝構造,移動構造函數,用法和普通構造函數一樣,這里不做詳述。還有一個比較特殊的構造函數:
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws
這個函數的在boost的幫助文檔中解釋為:constructs a shared_ptr
that shares ownership with r
and stores p(構造一個shared_ptr對象存儲p並且與r共享所有權),這個構造函數被稱為
aliasing constructor(不知道如何翻譯,aliasing有重疊的意思)。r是將要共享所有權的指針,p是實際指向的對象,構造的指針調用get()或者operator->將返回p,而不是r。為了更好的理解這個函數,我們考慮shared_ptr指針對象由兩個部分構成,一個是它的所有權(可以與其他指針共享的),另一個是它實際存儲的對象。在普通應用中,這兩部分是相同的。在由上述函數構造的shared_ptr中,兩個部分是不同的。當一個指針的引用計數為0時,如果它還與其他指針共享所有權,那么它實際存儲的對象不會被刪除,直到共享的引用計數為0。下面的例子會更直觀一些。
struct data {...}; struct object { data data_; }; void f () { shared_ptr<object> o (new object); // use_count == 1 shared_ptr<data> d (o, &o->data_); // use_count == 2 o.reset (); // use_count == 1 // When d goes out of scope, object is deleted. } void g () { typedef std::vector<object> objects; shared_ptr<objects> os (new objects); // use_count == 1 os->push_back (object ()); os->push_back (object ()); shared_ptr<object> o1 (os, &os->at (0)); // use_count == 2 shared_ptr<object> o2 (os, &os->at (1)); // use_count == 3 os.reset (); // use_count == 2 // When o1 goes out of scope, use_count becomes 1. // When o2 goes out of scope, objects is deleted. }
關於這一函數的應用參考:http://codesynthesis.com/~boris/blog/2012/04/25/shared-ptr-aliasing-constructor/
有關shared_ptr的其他成員函數參考:http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm#functions
3. 使用boost::shared_ptr的注意事項
(1) 不要把一個原生指針給多個shared_ptr管理
int* ptr = new int; boost::shared_ptr<int> p1(ptr); boost::shared_ptr<int> p2(ptr);
這樣做會導致ptr會被釋放兩次。在實際應用中,保證除了第一個shared_ptr使用ptr定義之外,后面的都采用p1來操作,就不會出現此類問題。
(2) 不要在函數實參里創建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷 //可能的過程是先new int,然后調g(),g()發生異常,shared_ptr<int>沒有創建,int內存泄露 //推薦寫法 shared_ptr<int> p(new int()); f(p, g());
(3) shared_ptr作為被保護的對象的成員時,小心因循環引用造成無法釋放資源。
簡單的例子:
class parent; class children; typedef boost::shared_ptr<parent> parent_ptr; typedef boost::shared_ptr<children> children_ptr; class parent { public: children_ptr children; }; class children {public: parent_ptr parent; }; void test() { boost::shared_ptr<parent> father( new parent); boost::shared_ptr<children> son(new children); father->children = son; //user_count() == 2 son->parent = father; //user_count() == 2 }
在這個例子中,出現了循環引用計數,賦值后use_count()變為2,出函數后變為1,資源無法被釋放。boost的解決方法是采用weak_ptr來保存。
class parent {public: boost::weak_ptr<children> children; }; class children {public: boost::weak_ptr<father> parent; };
因為boost不會影響weak_ptr不會影響引用計數,不會造成循環引用計數。
(4) 不要把this指針給shared_ptr
將this指針賦給shared_ptr會出現this指針被釋放兩次的危險,如下面的代碼,會在t釋放時析構一次,shared_ptr釋放時析構一次。
class test { public: boost::shared_ptr<test> pget() { return boost::shared_ptr<test>(this); } }; test t; boost::shared_ptr<test> pt = t.pget();
boost庫提供的解決方法是:使用enable_shared_from_this來實現。
class test : public boost::enable_shared_from_this<test> { public: boost::shared_ptr<test> pget() { return shared_from_this(); } }; test t; boost::shared_ptr<test> pt = t.pget();
4. std::tr1::shared_ptr和boost::shared_ptr
在新版本的C++標准中引用shared_ptr智能指針,名空間是std::tr1::shared_ptr。它和boost::shared_ptr的用法相同,在gcc4.3.x及以上的版本加選項-std=gnu++0x即可使用。
參考:
1. boost::shared_ptr參考:http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm
2. aliasing constructor函數原理及用法:http://codesynthesis.com/~boris/blog/2012/04/25/shared-ptr-aliasing-constructor/
3. std::tr1::shared_ptr參考:http://www.cplusplus.com/reference/memory/shared_ptr/
4. http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html