智能指針 --- 一

C++中的智能指針是用來管理外界資源的,如在堆上邊new的資源,當程序還沒來得及執行delete操作,函數遇到異常了,沒有釋放資源,導致內存泄露      如下代碼:

void fun()
{
    A *ptr = new A();
    /*...
    ...*/           //程序可能在這行代碼中拋出異常,導致ptr所指向堆上的資源沒有釋放
    delete ptr;
}

智能指針是怎麼解決這個問題的

智能指針在實現上也是一個類,即有構造函數、析構函數,在函數結束之前 類會自動調用析構函數,析構函數會釋放資源,所以當遇到異常時,資源也會被正確的釋放掉。

接下來介紹 auto_ptr、unique_ptr、 share_ptr、weak_ptr四種智能指針

auto_ptr 

auto_ptr是弱智能指針  一個資源只能被一個auto_ptr擁有,當智能指針賦值的時候擁有權就會發生轉移,如下代碼

因爲會發生所有權的轉移所以auto_ptr不能用在STL標準容器中

void Fun(auto_ptr<A> &ptr)
{
    cout << ptr -> a << endl;
    cout << "Fun faction end" << endl;
}

//auto_ptr
void Test()
{
    auto_ptr<A> aptr(new A(5));
    auto_ptr<A> bptr;
    bptr = aptr;                //賦值運算符重載函數
    auto_ptr<A> cptr(bptr);     //拷貝構造函數
    Fun(cptr);       //會導致aptr失效,將aptr對資源的擁有權轉向了ptr智能指針
    cout << cptr -> a << endl;
}

 

share_ptr:

share_ptr  資源可以被多個指針共享,share_ptr 使用引用計數對資源管理, 不可以調用release(), 調用reset()函數當前的share_ptr會釋放資源的所有權,引用計數減一,當引用計數爲0時,沒有指針指向該資源,資源會被釋放, 因爲資源可以被多個指針指向,所以指針可以賦值、在函數之間傳遞,存放在STL容器裏,  use_count() 返回當前指針指向資源的引用計數(use_count()的效率不高)。

share_ptr使用不當會出現循環引用,資源得不到釋放,暫時先不看這個問題。

下邊時使用share_ptr的相關代碼

示範一:

class A{};
shared_ptr<A> aptr(new A());
shared_ptr<A> bptr = aptr;
    
cout << aptr.use_count() << endl;

輸出

A()
2
~A()

示範二:

A *a = new A();
shared_ptr<A> aptr(a);
shared_ptr<A> bptr = aptr;

//這樣寫會報錯,導致堆上的同一資源被delete了兩次
/*A *a = new A();
shared_ptr<A> aptr(a);
shared_ptr<A> bptr(a);*/

unique_ptr

c++11棄用了auto_ptr,unique_ptr替代了auto_ptr,在同一時刻只有一個unique_ptr指向給定的對象,該智能指針是禁止使用拷貝構造函數的 禁止使用賦值運算符重載函數 

  • 只能使用move()函數對對象的所有權進行轉移,
  • reset()函數用來重新指定資源
  • release 釋放所有權
void Fun(unique_ptr<A>& ptr)   //必須寫成引用
{
    cout<< ptr -> a << endl;
}

//unique_ptr
void Test1()
{
    unique_ptr<A> aptr(new A(10));
    unique_ptr<A> bptr;
//    bptr = aptr;                //error
//    unique_ptr<A> cptr(aptr);   //error
    bptr = move(aptr);
    cout<< bptr -> a << endl;
    aptr.reset(new A(20));
    cout<< aptr -> a << endl;
    Fun(aptr);
    aptr.release();
//    cout<< aptr -> a << endl;  //error
    cout<< "Test1() end" <<endl;
}

 

weak_ptr

weak_ptr 弱智能指針,能看到資源的引用計數,但是不會去用這個引用計數,使用weak_ptr指向資源不會使引用計數增加。

爲了配合shared_ptr而引入了weak_ptr,weak_ptr是從share_ptr 或 從另一個weak_ptr對象那裏構造,不能直接指向一個新new出來的資源,weaker_ptr可以觀察資源的引用情況,他沒有重載* 和 -> 運算符。   use_count()函數返回資源的引用計數。 在weak_ptr智能指針類中可以有一個成員函數 lcok();  該函數的作用是從被觀測的對象返回一個可用的shared_ptr對象,可以將這個對象賦值給一個新的shred_ptr智能指針.

示範代碼如下:

//weak_ptr
void Test2()
{
    shared_ptr<A> aptr(new A(10));
    shared_ptr<A> bptr(aptr);
    weak_ptr<A> wptr(aptr);
    cout << wptr.use_count() << endl;
    shared_ptr<A> cptr = wptr.lock();    //獲得shared_ptr對象
    cout << cptr -> a << endl;
    cout << wptr.use_count() << endl;    //獲得引用計數

    aptr.reset(new A(20));               //重新指向一個新的資源
    cout << aptr -> a << endl;


    aptr.reset();                        //指向一個空的資源
    bptr.reset();
    cptr.reset();
    cout << wptr.use_count() << endl;
    shared_ptr<A> dptr = wptr.lock();   //當堆上的對象引用計數爲0,被釋放的時候,weak_ptr調用lock()返回一個指針空值(nullptr)
    cout << dptr.use_count() << endl;

}

在所有的智能指針中析構函數調用的是delete,不是delete[]; 所以在創建智能指針的時候不可以寫成 new A[10]; 不能new一個數組,導致資源釋放的時候發生錯誤

整體的代碼稍後傳到Github上

下一篇總結強智能指針shared_ptr引起的循環引用,weak_ptr配合shared_ptr的案例之一.

之後會把Effective C++上對智能指針的條例總結到後續博客中

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