有關智能指針(shared_ptr)的討論


1. boost::shared_ptr的用法
2. boost::shared_ptr的實現機制
3. 使用boost::shared_ptr的注意事項
4. std::tr1::shared_ptr和boost::shared_ptr
5. 參考

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM