(六)boost庫以內存管理shared_ptr

一、shared_ptr的基本用法

    boost::shared_ptr<int> sp(new int(10));     //一個指向整數的shared_ptr
    assert(sp.unique());                        //如今shared_ptr是指針的惟一持有者
    boost::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再也不持有任何指針(空指針)
    assert(sp2.use_count() == 1);               //sp2引用計數變爲1
    sp.reset(new int(20));                      //sp管理一個新對象
    assert(*sp == 20);

 

二、應用於標準容器

    有兩種方式能夠將shared_ptr應用於標準容器(或者容器適配器等其餘容器)。程序員

    一種用法是將容器做爲shared_ptr管理的對象,如shared_ptr<list<T> >,使容器能夠被安全地共享,用法與普通shared_ptr沒有區別,咱們再也不討論。編程

    另外一種用法是將shared_ptr做爲容器的元素,如vector<shared_ptr<T> >,由於shared_ptr支持拷貝語義和比較操做,符合標準容器對元素的要求,因此能夠實如今容器中安全地容納元素的指針而不是拷貝。windows

    標準容器不能容納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[]用法與迭代器相似,也須要使用*獲取真正的值。this

三、使用助手類enable_shared_from_this

    爲何要使用enable_shared_from_this,或許你對這個類感到很迷惑,先看看下面這種狀況:spa

class MyPoint
{
public:
    MyPoint(){std::cout << "MyPoint" << std::endl;}
    ~MyPoint(){std::cout << "~MyPoint" << std::endl;}
    //返回this的函數
    boost::shared_ptr<MyPoint> GetPoint()
    {
        return boost::shared_ptr<MyPoint>(this);   //錯誤,將返回一個新的引用計數
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    boost::shared_ptr<MyPoint> p1(new MyPoint);    
    boost::shared_ptr<MyPoint> p2 = p1->GetPoint(); 
    std::cout << p1.use_count() << "," << p2.use_count() << std::endl;  //輸出引用計數狀況
    p1.reset();          //內存將被釋放
}

咱們獲得的答案將是:線程

MyPoint
1,1
~MyPoint指針

怎麼正確的返回this呢,那麼就須要藉助enable_shared_from_this了,引入enable_shared_from_this的緣由是能夠實現返回值爲指向該類自己的shared_ptrcode

正確的寫法應該是這樣的:

class MyPoint : public boost::enable_shared_from_this<MyPoint>
{
public:
    MyPoint(){std::cout << "MyPoint" << std::endl;}
    ~MyPoint(){std::cout << "~MyPoint" << std::endl;}
    //返回this的函數
    boost::shared_ptr<MyPoint> GetPoint()
    {
        return shared_from_this();
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
     boost::shared_ptr<MyPoint> p1(new MyPoint);    
     boost::shared_ptr<MyPoint> p2 = p1->GetPoint(); 
     std::cout << p1.use_count() << "," << p2.use_count() << std::endl;  //輸出引用計數狀況
     p1.reset();          //內存將被釋放
}

四、定製刪除器

當你在使用windows API函數進行編程時,你最煩的或許就是怎麼保證申請的內核對象是否關閉,考慮一下代碼:

    int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) );
    //業務處理
    //......
    HeapFree( GetProcessHeap(), 0, p );

對象不能經過delete來刪除,而是一個釋放函數,shared_ptr可否勝任呢,答案是確定的。

    int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) );
    //此處使用了lambda表達式,須要vs2010或更高版本的支持
    boost::shared_ptr<int> ptr(p, [](int *p){HeapFree( GetProcessHeap(), 0, p );});

五、綜合應用示例

下面實現一個線程類,在線程運行結束時,可以自行清理本身的內存

#include <set>
#include <Windows.h>
#include <boost/thread.hpp>
class MySelf;
std::set< boost::shared_ptr<MySelf> > myList;
boost::thread *ptrTh;
class MySelf: public boost::enable_shared_from_this<MySelf>
{
public:
    MySelf()
    {
        printf("MySelf\n");
    }
    void StartThread()
    {
        //啓動一個線程    
        ptrTh = new boost::thread(&MySelf::Run, this);
    }
    void Run()
    {
        //線程任務函數
        Sleep(5000);
        printf("stop thread\n");
        Stop();
    }
    void Stop()
    {
        //刪除本身
        myList.erase(shared_from_this());
    }
    ~MySelf()
    {
        printf("~MySelf\n");
    }
};
void TestSharePtr()
{
    boost::shared_ptr<MySelf> ptr1(new MySelf);
    //保存到list中
    myList.insert(ptr1);
    ptr1->StartThread();
}
相關文章
相關標籤/搜索