shared_ptr源碼解讀

來源:互聯網
上載者:User

1  介紹
眾所周知,auto_ptr由於它的破壞性複製語義,無法滿足標準容器對元素的要求,因而不能放在標準容器中;如果我們希望當容器析構時能自動把它容納的指標元素所指的對象刪除時,通常採用一些間接的方式來實現,顯得比較繁瑣。boost庫中提供了一種新型的智能指標 shared_ptr,它解決了在多個指標間共用對象所有權的問題,同時也滿足容器對元素的要求,因而可以安全地放入容器中。現在我們可以這樣寫:

vector< shared_ptr<Book> > books;
books.push_back( shared_ptr<Book>(new Book()) );

2  實現
shared_ptr是怎麼實現的?讓我們分析一下它的源碼吧:
按照上面代碼,在建立了Book對象之後,會建立一個臨時的shared_ptr<Book>對象,shared_ptr的建構函式被調用:

template<class Y>
explicit shared_ptr(Y * p)
  : px(p), pn(p, checked_deleter<Y>())
{
    detail::sp_enable_shared_from_this(p, p, pn);
}
顯然這是個模板函數,推匯出的Y應該為Book類型,px和pn為shared_ptr的成員變數,其聲明如下:

template<class T> class shared_ptr
{
private:
    T * px;                     // contained pointer
    detail::shared_count pn;        // reference counter
}
由於我們是用Book為模板參數執行個體化shared_ptr(shared_ptr<Book>),px應該為 Book* 類型,px(p)把px初始化為指向建立的Book 對象。類型為shared_count的pn成員變數以兩個參數初始化,一個是傳入的Book對象指標,另一個是一個臨時的  check_deleter<Book> 對象,shared_count的建構函式被調用:

template<class P, class D>
shared_count(P p, D d)
: pi_(0)
{
    try
    {
        pi_ = new sp_counted_base_impl<P, D>(p, d);
    }
    catch(...)
    {
        d(p); // delete p
        throw;
    }
}
應該推匯出P為Book*類型,D為checked_deleter<Book>類型。在shared_count的建構函式體,建立了一個  sp_counted_base_impl<P,D> 的對象,以傳入的 Book 對象指標和
 check_deleter<Book> 對象為建構函式參數。看起來 sp_counted_base_impl 像是一個實作類別:

template<class P, class D>
class sp_counted_base_impl: public sp_counted_base
{
private:
    P ptr; // copy constructor must not throw
    D del; // copy constructor must not throw
public:
    sp_counted_base_impl(P p, D d): ptr(p), del(d)
    {
    }
};
sp_counted_base的建構函式應該先被調用:
class sp_counted_base {
public:
    sp_counted_base(): use_count_(1), weak_count_(1)
    {
    }
private:
    long use_count_;        // #shared
    long weak_count_;       // #weak + (#shared != 0)
};
看來sp_counted_base是儲存引用計數的地方,這裡use_count_和weak_count_被初始化為1。接下來  sp_counted_base_impl 的建構函式儲存了 Book 對象的指標和 check_deleter<Book> 對象。用類圖把幾個主要類之間的關係表示出來:

當books.push_back被調用時,應該發生一次拷貝構造:

template<class Y>
shared_ptr(shared_ptr<Y> const & r)
    : px(r.px),pn(r.pn) // never throws
{ }
pn為shared_count類型,它的拷貝建構函式又被調用:
shared_count(shared_count const & r)
    : pi_(r.pi_) // nothrow
{
    if(pi_!= 0) pi_->add_ref_copy();
}
可見新建立的shared_ptr對象和被拷貝的shared_ptr對象擁有了指向同一個 sp_counted_base 對象的指標,並通過調用  sp_count_base 的add_ref_copy函數把引用計數加一:

void add_ref_copy() { ++use_count_; }
用對象圖把此時刻的對象關係表示出來:

當臨時share_ptr對象被複製到容器中後,它就應該被銷毀了,由於沒有顯式定義解構函式,編譯器產生的解構函式會析構pn成員:

~shared_count() // nothrow
{
    if(pi_ != 0) pi_->release();
}
shared_counted_base的release函數:
void release() // nothrow
{
    {
        long new_use_count = --use_count_;
       if(new_use_count != 0) return;
    }
    dispose();
    weak_release();
}
這裡把sp_counted_base儲存的引用計數減一,引用計數減為1而返回。於是臨時建立的shared_ptr對象被銷毀了,但是Book對象並沒有被銷毀。當vector中的shared_ptr對象也被析構時,引用計數會減為0,於是執行dispose函數:

virtual void dispose() = 0; // nothrow
純虛函數?別忘了pi_實際指向的物件類型為 sp_counted_base_impl ,所以 sp_counted_base_impl 的  dispose 函數被調用:

virtual void dispose() // nothrow
{
    del(ptr);
}
根據前面分析,del是一個check_deleter<Book>對象:

template<class T>
struct checked_deleter {
    typedef void result_type;
    typedef T * argument_type;
    void operator()(T * x) const
    {
        // boost:: disables ADL
        boost::checked_delete(x);
    }
};
好像是個function object,傳入了Book對象的指標,通過調用 check_delete 函數從而刪除了 shared_ptr 管理的Book對象。可以看到check_deleter沒有任何成員變數,因而上面發生的幾次拷貝構造應該是沒有開銷的。sp_counted_base和 sp_counted_base_impl好像用了什麼模式?對了,就是template method,它使引用計數的演算法獨立於對象刪除的實現細節而被重用。
shared_ptr是安全執行緒的嗎?是的,它使用了一種輕量級的mutex做同步,為簡單起見我沒有列出相關的代碼,實際是比較簡單的。至於效率,好像多了很多操作,會不會比較慢?經驗告訴我們:不要做事先最佳化。當真正發現效能瓶頸時再去做最佳化會更有的放矢。對於一些資源,以指標方式表示,但需要以特定的方式釋放,比如檔案控制代碼,shared_ptr有沒有辦法管理?答案是肯定的,你只需要提供一個函數或function object來實現那個特定的釋放邏輯,然後在構造shared_ptr對象時傳進去就可以了:

void file_closer(FILE* f) { fclose(f); }
shared_ptr<FILE> file(fopen("\a.txt", "r"), file_closer);
這裡,shared_ptr的第二個模板建構函式就起作用了:

template<class Y,
class D> shared_ptr(Y * p, D d)
    : px(p), pn(p, d)
{
    detail::sp_enable_shared_from_this(p, p, pn);
}
推匯出D應該為void(*)(FILE*)函數指標類型,當在該檔案控制代碼上的引用計數為0時,file_closer就會被調用,從而關閉檔案。

3  結論
shared_ptr解決了在多個指標間共用對象所有權的問題,它可以安全的放入標準容器,並且是安全執行緒的。

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/dadalan/archive/2009/08/11/4434885.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.