shared_ptr

shared_ptr是一個最像指針的”智能指針”,是boost.smart_ptr庫中最有價值、最重要的組成部分,也是最有用的,Boost庫的許多組件–甚至還包括其他一些領域的智能指針都使用了shared_ptr。抱歉,我實在想不出什麼更恰當的詞彙來形容它在軟件開發中的重要性。再強調一遍,shared_ptr非常有價值、非常重要、非常有用。

shared_ptr與scoped_ptr一樣包裝了new操作符在堆上分配的動態對象,但它實現的是引用計數型的智能指針 ,可以被自由地拷貝和賦值,在任意的地方共享它,當沒有代碼使用(引用計數爲0)它時才刪除被包裝的動態分配的對象。shared_ptr也可以安全地放到標準容器中,並彌補了auto_ptr因爲轉移語義而不能把指針作爲STL容器元素的缺陷。

在C++歷史上曾經出現過無數的引用計數型智能指針實現,但沒有一個比得上boost::shared_ptr,在過去、現在和將來,它都是最好的。

  1. shared_ptr的線程安全性

    shared_ptr 本身不是 100% 線程安全的。它的引用計數本身是安全且無鎖的,但對象的讀寫則不是,因爲 shared_ptr 有兩個數據成員,讀寫操作不能原子化。根據文檔,shared_ptr 的線程安全級別和內建類型、標準庫容器、string 一樣,即:

    • 一個 shared_ptr 實體可被多個線程同時讀取
    • 兩個的 shared_ptr 實體可以被兩個線程同時寫入,“析構”算寫操作
    • 如果要從多個線程讀寫同一個 shared_ptr 對象,那麼需要加鎖。
  2. shared_ptr用法


shared_ptr<int> sp(new int(10));                //一個指向整數的shared_ptr  
assert(sp.unique());                            //現在shared_ptr是指針的唯一持有者   
shared_ptr<int> sp2 = sp;                       //第二個shared_ptr,拷貝構造函數   
assert(sp == sp2 && sp.use_count() == 2);       //兩個shared_ptr相等,指向同一個對象,引用計數爲2  
*sp2 = 100;                                     //使用解引用操作符修改被指對象  
assert(*sp == 100);                             //另一個shared_ptr也同時被修改   
sp.reset();                                     //停止shared_ptr的使用  
assert(!sp);                                    //sp不再持有任何指針(空指針)

class shared                                    //一個擁有shared_ptr的類  
{  
private:      
    shared_ptr<int> p;                          //shared_ptr成員變量  
public:      
    shared(shared_ptr<int> p_):p(p_){}          //構造函數初始化shared_ptr      
    void print()                                //輸出shared_ptr的引用計數和指向的值      
    {          cout << "count:" << p.use_count()              
    << "v =" <<*p << endl;      
    }  
};  
void print_func(shared_ptr<int> p)              //使用shared_ptr作爲函數參數  
{       
    //同樣輸出shared_ptr的引用計數和指向的值      
    cout << "count:" << p.use_count()          
        << " v=" <<*p << endl;  }  
int main()  
{      
    shared_ptr<int> p(new int(100));      
    shared s1(p), s2(p);                        //構造兩個自定義類       
    s1.print();      
    s2.print();       
    *p = 20;                                    //修改shared_ptr所指的值      
    print_func(p);      
    s1.print();  
} 
  1. 應用於標準容器

有兩種方式可以將shared_ptr應用於標準容器(或者容器適配器等其他容器)。

一種用法是將容器作爲shared_ptr管理的對象,如shared_ptr<list<T> >,使容器可以被安全地共享,用法與普通shared_ptr沒有區別,我們不再討論。

另一種用法是將shared_ptr作爲容器的元素,如vector<shared_ptr<T> >,因爲shared_ptr支持拷貝語義和比較操作,符合標準容器對元素的要求,所以可以實現在容器中安全地容納元素的指針而不是拷貝。

標準容器不能容納auto_ptr,這是C++標準特別規定的(讀者永遠也不要有這種想法)。標準容器也不能容納scoped_ptr,因爲scoped_ptr不能拷貝和賦值。標準容器可以容納原始指針,但這就喪失了容器的許多好處,因爲標準容器無法自動管理類型爲指針的元素,必須編寫額外的大量代碼來保證指針最終被正確刪除,這通常很麻煩很難實現。

存儲shared_ptr的容器與存儲原始指針的容器功能幾乎一樣,但shared_ptr爲程序員做了指針的管理工作,可以任意使用shared_ptr而不用擔心資源泄漏。

#include <boost/make_shared.hpp> 
int main()  
{      
    typedef vector<shared_ptr<int> > vs;    //一個持有shared_ptr的標準容器類型      
    vs v(10);                               //聲明一個擁有10個元素的容器,元素被初始化爲空指針       
    int i = 0;      
    for (vs::iterator pos = v.begin(); pos != v.end(); ++pos)      
    {          
        (*pos) = make_shared<int>(++i);     //使用工廠函數賦值          
        cout << *(*pos) << ", ";            //輸出值      
    }      
    cout << endl;       
    shared_ptr<int> p = v[9];      
    *p = 100;      
    cout << *v[9] << endl;  
} 

這段代碼需要注意的是迭代器和operator[]的用法,因爲容器內存儲的是shared_ptr,我們必須對迭代器pos使用一次解引用操作符以獲得shared_ptr,然後再對shared_ptr使用解引用操作符才能操作真正的值。(*pos)也可以直接寫成**pos,但前者更清晰,後者很容易讓人迷惑。vector的operator[]用法與迭代器類似,也需要使用獲取真正的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章