boost shared ptr的使用方法
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: 1a1 reference count: 2a2 reference count: 2a2 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 throwsshared_ptr(std::nullptr_t); // never throwstemplate<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.}
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即可使用。