【轉】STL 四種智能指針

STL 一共給咱們提供了四種智能指針:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,auto_ptr 是 C++98 提供的解決方案,C+11 已將其摒棄,並提出了 unique_ptr 做爲 auto_ptr 替代方案。雖然 auto_ptr 已被摒棄,但在實際項目中仍可以使用,但建議使用較新的 unique_ptr,由於 unique_ptr 比 auto_ptr 更加安全,後文會詳細敘述。shared_ptr 和 weak_ptr 則是 C+11 從準標準庫 Boost 中引入的兩種智能指針。此外,Boost 庫還提出了 boost::scoped_ptr、boost::scoped_array、boost::intrusive_ptr 等智能指針,雖然還沒有獲得 C++ 標準採納,可是在開發實踐中可使用。ios

auto_ptr

auto_ptr 一樣是 STL 智能指針家族的成員之一,由 C++98 引入,定義在頭文件 。其功能和用法相似於 unique_ptr,由 new expression 得到對象,在 auto_ptr 對象銷燬時,他所管理的對象也會自動被 delete 掉。
auto_ptr 從 C++98 使用至今,從 C++11 開始,引入unique_ptr 來替代 auto_ptr。
先來看下面的賦值語句:
算法

auto_ptr<string> ps (new string ("I reigned lonely as a cloud.」);
auto_ptr<string> vocation; 
vocaticn = ps;

若是 ps 和 vocation 是常規指針,則兩個指針將指向同一個 string 對象。這是不能接受的,由於程序將試圖刪除同一個對象兩次,一次是 ps 過時時,另外一次是 vocation 過時時。
要避免這種問題,方法有多種:express

一、定義陚值運算符,使之執行深複製。這樣兩個指針將指向不一樣的對象,其中的一個對象是另外一個對象的副本,缺點是浪費空間,因此智能指針都未採用此方案。
二、創建全部權(ownership)概念。對於特定的對象,只能有一個智能指針可擁有,這樣只有擁有對象的智能指針的析構函數會刪除該對象。而後讓賦值操做轉讓全部權。這就是用於 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更嚴格。
三、建立智能更高的指針,跟蹤引用特定對象的智能指針數。這稱爲引用計數。例如,賦值時,計數將加 1,而指針過時時,計數將減 1,。當減爲 0 時才調用 delete。這是 shared_ptr 採用的策略。

固然,一樣的策略也適用於複製構造函數,即auto_ptr vocation(ps)時也須要上面的策略。每種方法都有其用途,但爲什麼要摒棄 auto_ptr 呢? 安全

下面舉個例子來講明。請看以下代碼:函數

#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    auto_ptr<string> films[5] ={
    auto_ptr<string> (new string("Fowl Balls")),
    auto_ptr<string> (new string("Duck Walks")),
    auto_ptr<string> (new string("Chicken Runs")),
    auto_ptr<string> (new string("Turkey Errors")),
    auto_ptr<string> (new string("Goose Eggs"))
    };
    auto_ptr<string> pwin;
    pwin = films[2]; // films[2] loses ownership. 將全部權從films[2]轉讓給pwin,此時films[2]再也不引用該字符串從而變成空指針

    cout << "The nominees for best avian baseballl film are\n";
    for(int i = 0; i < 5; ++i)
    {
        cout << *films[i] << endl;
    }
    cout << "The winner is " << *pwin << endl;
    return 0;
}

運行下發現程序崩潰了,緣由在上面註釋已經說的很清楚,films[2] 已是空指針了,下面輸出訪問空指針固然會崩潰了。但這裏若是把 auto_ptr 換成 shared_ptr 後,程序就不會崩潰,緣由以下:
使用 shared_ptr 時運行正常,由於 shared_ptr 採用引用計數,pwin 和films[2] 都指向同一塊內存,在釋放空間時由於事先要判斷引用計數值的大小所以不會出現屢次刪除一個對象的錯誤。
使用 unique_ptr 時編譯出錯,與 auto_ptr 同樣,unique_ptr 也採用全部權模型,但在使用 unique_ptr 時,程序不會等到運行階段崩潰,而在編譯期因下述代碼行出現錯誤:spa

unique_ptr<string> pwin;
pwin = films[2];                    //films[2] loses ownership

指導你發現潛在的內存錯誤。這就是爲什麼要摒棄 auto_ptr 的緣由,一句話總結就是:避免因潛在的內存問題致使程序崩潰。
從上面可見,unique_ptr 比 auto_ptr 更加安全,由於 auto_ptr 有拷貝語義,拷貝後原對象變得無效,再次訪問原對象時會致使程序崩潰;unique_ptr 則禁止了拷貝語義,但提供了移動語義,便可以使用std::move() 進行控制權限的轉移,以下代碼所示:指針

unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt);   //編譯出錯,已禁止拷貝
unique_ptr<string> upt1=upt;    //編譯出錯,已禁止拷貝
unique_ptr<string> upt1=std::move(upt);  //控制權限轉移

auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt); //編譯經過
auto_ptr<string> apt1=apt;  //編譯經過

這裏要注意,在使用std::move將unique_ptr的控制權限轉移後,不可以再經過unique_ptr來訪問和控制資源了,不然一樣會出現程序崩潰。咱們能夠在使用unique_ptr訪問資源前,使用成員函數get()進行判空操做。code

unique_ptr<string> upt1=std::move(upt);                             //控制權限轉移
if(upt.get()!=nullptr)                                                              //判空操做更安全
{
    //do something
}

2.unique_ptr

unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。unique_ptr 是一種定義在頭文件<memory>中的智能指針。它持有對對象的獨有權——兩個unique_ptr不能指向一個對象,即 unique_ptr 不共享它所管理的對象。它沒法複製到其餘 unique_ptr,沒法經過值傳遞到函數,也沒法用於須要副本的任何標準模板庫 (STL)算法。只能移動 unique_ptr,即對資源管理權限能夠實現轉移。這意味着,內存資源全部權能夠轉移到另外一個 unique_ptr,而且原始 unique_ptr 再也不擁有此資源。實際使用中,建議將對象限制爲由一個全部者全部,由於多個全部權會使程序邏輯變得複雜。所以,當須要智能指針用於存 C++ 對象時,可以使用 unique_ptr,構造 unique_ptr 時,可以使用 make_unique Helper 函數。對象

下圖演示了兩個 unique_ptr 實例之間的全部權轉換。

unique_ptr與原始指針同樣有效,並可用於 STL 容器。將 unique_ptr 實例添加到 STL 容器頗有效,由於經過 unique_ptr 的移動構造函數,再也不須要進行復制操做。unique_ptr指針與其所指對象的關係:在智能指針生命週期內,能夠改變智能指針所指對象,如建立智能指針時經過構造函數指定、經過reset方法從新指定、經過release方法釋放全部權、經過移動語義轉移全部權,unique_ptr還可能沒有對象,這種狀況被稱爲empty。
unique_ptr的基本操做有:blog

//智能指針的建立  
unique_ptr<int> u_i; //建立空智能指針
u_i.reset(new int(3)); //"綁定」動態對象  
unique_ptr<int> u_i2(new int(4));//建立時指定動態對象
unique_ptr<T,D> u(d);   //建立空unique_ptr,執行類型爲T的對象,用類型爲D的對象d來替代默認的刪除器delete

//全部權的變化  
int *p_i = u_i2.release(); //釋放全部權  
unique_ptr<string> u_s(new string("abc"));  
unique_ptr<string> u_s2 = std::move(u_s); //全部權轉移(經過移動語義),u_s全部權轉移後,變成「空指針」 
u_s2.reset(u_s.release());//全部權轉移
u_s2=nullptr;//顯式銷燬所指對象,同時智能指針變爲空指針。與u_s2.reset()等價

unique_ptr不只安全,並且靈活

若是unique_ptr 是個臨時右值,編譯器容許拷貝語義。參考以下代碼:

unique_ptr<string> demo(const char * s){
    unique_ptr<string> temp (new string (s)); 
    return temp;
}

//假設編寫了以下代碼:
unique_ptr<string> ps;
ps = demo('Uniquely special");

demo()返回一個臨時unique_ptr,而後ps接管了本來歸返回的unique_ptr全部的對象,而返回時臨時的 unique_ptr 被銷燬,也就是說沒有機會使用 unique_ptr 來訪問無效的數據,換句話來講,這種賦值是不會出現任何問題的,即沒有理由禁止這種賦值。實際上,編譯器確實容許這種賦值。相對於auto_ptr任何狀況下都容許拷貝語義,這正是unique_ptr更加靈活聰明的地方。

相關文章
相關標籤/搜索