智能指針小分析

咱們爲何須要智能指針

所謂資源就是,一旦用了它,未來必須還給系統。C++中內存資源的動態分配經由new與delete實現。問題在於,不管是有意無心,咱們有時候總會忘記釋放內存中的資源。例如delete語句出如今某個循環語句中,而咱們的continue或者break卻跳過了它的執行;或者是在程序中某個分支含有函數return語句,而delete操做放在return 語句以後;更加難以預料的事情是程序執行過程當中發生了異常,致使咱們的delete語句沒有執行。總的來講,把資源回收交給用戶並非一種好作法。咱們指望有一種機制,它幫助咱們管理從系統獲取而來的資源,當咱們再也不使用該資源時,該機制能自動幫咱們回收,避免了內存泄漏問題。智能指針就是這樣一種資源回收機制。程序員

智能指針具體是什麼

《Effective C++》條款13提到,以對象來管理資源。這個條款提到了兩個觀點:數組

  1. 得到資源後馬上放進管理對象內。
  2. 管理對象運行析構函數確保資源被釋放。

智能指針就是這樣的一種類。它們的行爲相似於指針,一樣支持解引用* 或取成員->運算。智能指針將基本內置類型指針封裝爲類對象指針,管理着該指針所指向的動態分配資源,並經過類的析構函數對資源進行釋放。在C++中,智能指針都是模板類,由於它們要管理的多是用戶自定義類型所分配的內存空間。安全

智能指針的實現原理

在STL中,一共是有四種智能指針:auto_ptr,unique_ptr,shared_ptr,weak_ptr。其中auto_ptr是C++98提供的智能指針,如今基本已經被棄用。緣由後面有說。
其中auto_ptr,unique_ptr是獨佔型的智能指針。這裏以auto_ptr爲例,在某個時刻下,只能有一個auto_ptr指向一個給定的對象。shared_ptr則容許多個指針指向同一個對象,而weak_ptr指向的是shared_ptr所管理的對象,它是一種弱引用。
shared_ptr的實現基於引用計數技術。智能指針管理的着一個對象,並記錄着全部管理同個對象的指針個數,這個個數稱爲計數。藉由智能指針去初始化或賦值其餘智能指針時,計數的值增長1,表示資源對象多了一個引用;當智能指針的生命週期結束或者指向別的對象時,計數的值減去1,表示資源對象減小一個引用。智能指針生命週期結束時,編譯器會調用它的析構函數,在析構函數中判斷引用計數的值是否爲0,若爲0,則釋放所管理的對象資源;若不爲0,代表還有其餘指針指向所管理的對象,不釋放該對象資源。函數

爲何要摒棄auto_ptr

上面說到auto_ptr是C++98提供的智能指針,如今已經被摒棄,緣由在於爲了維護獨佔性,auto_ptr進行了不正常的複製/賦值行爲
咱們的賦值操做在語義上保證了右操做數不會在賦值時受到修改,然而,爲了保證auto_ptr的獨佔性,這種語義被修改了。3d

auto_ptr<int> p1(new int(1));  /*1*/
auto_ptr<int> p2(p1); /*2*/
auto_ptr<int> p3; 
p3= p2;/*3*/

p1 開始管理着值爲1的對象,執行了2以後,p1被置空,由p2獨佔對象資源;執行3以後,p2被置爲空,由p3獨佔對象資源。想象有一個元素爲auto_ptr的數組:指針

auto_ptr<int>vec[5]=
    {
        auto_ptr<int>(new int(1)),
        auto_ptr<int>(new int(2)),
        auto_ptr<int>(new int(3)),
        auto_ptr<int>(new int(4)),
        auto_ptr<int>(new int(5)),
    };
    for (auto & t : vec)
    {
        cout << *t << endl;
    }
    //vec[2]被置空
    auto_ptr<int> aptr = vec[2];
   //程序運行奔潰
    for (auto & t : vec)
    {
        cout << *t << endl;
    }

而咱們的STL容器要求其元素能夠有正常的複製行爲,所以,STL容器容不得auto_ptr。而C++11新出現的智能指針unique_ptr比auto_ptr更聰明好用,unique_ptr拒絕直接的複製/賦值操做,必須經過reset/release接口來進行對象管理權的轉移,這無疑提升了安全性;unique_ptr的聰明還體如今:code

unique_ptr test()
{
    unique_ptr <int> temp(new int (1));
    return temp;
}

unique_ptr<int> p;
p = test();

在這裏test返回的臨時變量對p的賦值操做成功,由於臨時變量複製結束後就被銷燬,沒有機會經過臨時的unique_ptr對象去訪問無效數據,這種賦值是安全的。
總結一下:對象

  1. auto_ptr不適用於STL容器,且易形成對無效指針的訪問致使程序奔潰。
  2. unique_ptr比auto_ptr更加智能,安全性更高,應該選擇使用unique_ptr。

weak_ptr有何做用

weak_ptr是一種不控制所指向對象生命期的智能指針,它指向由一個shared_ptr管理的對象。講一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數,一旦最後一個指向對象的shared_ptr被銷燬,對象就會被釋放,即便有weak_ptr指向對象,對象仍是會被釋放。weak_ptr也取名自這種弱共享對象的特色。blog

相對於weak_ptr來講 ,shared_ptr是一種強引用的關係。在循環引用的狀況下資源得不到回收,將形成內存泄漏。以下圖出現了引用計數的循環引用問題:對象A被對象B所引用,對象C被對象A所引用,對象B被對象C所引用,這時每一個對象的引用計數都是1,都在等待在引用它的對象釋放對象,形成一種循環等待的現象,而資源也不會被如願釋放掉。接口

weak_ptr弱引用的出現正是可以打破這種循環引用。因爲弱引用不更改引用計數,相似普通指針,只要把循環引用的一方使用弱引用,便可解除循環引用。雖然經過弱引用指針能夠有效的解除循環引用,但這種方式必須在程序員能預見會出現循環引用的狀況下才能使用,也能夠是說這個僅僅是一種編譯期的解決方案,若是程序在運行過程當中出現了循環引用,仍是會形成內存泄漏的。所以,即便使用了智能指針,C++仍是沒法徹底杜絕內存泄漏的問題。

相關文章
相關標籤/搜索