程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++ 智能指針

C++ 智能指針

編輯:關於C++

介紹

C++提供了4種智能指針用於對分配的內存進行自動釋放,這些智能指針如下:auto_ptr、unique_ptr、shared_ptr、weak_ptr。其中auto_ptr在C++98標准引入,後三種在C++11標准中加入。而auto_ptr已經被C++11所摒棄,建議使用後三種智能指針,這4種智能指針使用模板(template)實現。

\

C++11將boZ喎?/kf/ware/vc/" target="_blank" class="keylink">vc3TA77XE1eLSu8zXxMnI68HLserXvKGjPC9wPg0KPGgyIGlkPQ=="1autoptr">1.auto_ptr

auto_ptr的構造函數接受new操作符或者對象工廠創建出的對象指針作為參數,從而代理了原始指針。雖然它是一個對象,但因為重載了 operator*後operator->,其行為非常類似指針,可以把它用在大多數普通指針可用的地方。當退出作用域時(離開作用域或異常),C++會保證auto_ptr對象銷毀,調用auto_ptr的析構函數,進而使用delete操作符刪除原始指針釋放資源。

auto_ptr很好用,被包含在C++標准庫中令它在世界范圍內被廣泛使用,使用智能指針的思想、用法深入人心。但標注庫沒有覆蓋智能指針的全部領域,尤其最重要的引用計數型智能指針。

2.unique_ptr

官方文檔 地址

unique_ptr為何優於auto_ptr

請看下面的語句:

auto_ptr p1 (new string("auto"));  //#1
auto_ptr p2;                       //#2
p2 = p1;                                   //#3                                          

在語句#3中,p2接管string對象的所有權後,p1的所有權將被剝奪。這是件好事,可以防止p1和p2的析構函數試圖刪除同一個對象;但如果程序隨後試圖使用p1,這將是件壞事,因為p1不再指向有效的數據。

下面來看看使用unique_ptr的情況:

unique_ptr p3 (new string("auto"));  //#4
unique_ptr p4;                       //#5            
p4 = p3;                                     //#6                      

編譯器認為語句#6非法,避免了p3不再指向有效數據的問題。因此,unique_ptr比auto_ptr更安全(編譯階段錯誤比潛在的程序崩潰更安全)。

但有時候,將一個智能指針賦值給另一個並不會留下危險的懸掛指針(就是空指針,極有可能被誤用)。假設有如下函數定義:

#include 
#include 
#include 
using std::string;
using std::cout;
using std::unique_ptr;
class Report
{
private:
    string str;
public:
    Report( const string s):str(s) { cout << "Object created!\n"; }
    ~Report() { cout << "Object deleted!\n"; }
    void comment(const string owner) const {
           cout << owner << str << "\n";
     }
};
unique_ptr demo(const char *s)
{
    unique_ptr temp(new Report(s));
    return temp;
}
int main(void)
{
    unique_ptr ps;
    ps = demo("Uniquely special point");
    ps->comment(string("un_ptr:"));
    return 0;
}

demo( )返回一個臨時的unique_ptr,然後ps接管了原本歸返回的unique_ptr所有的對象,而返回的unique_ptr被銷毀。這沒有問題,因為ps擁有了Report對象的所有權。這裡還有另一個好處是,demo()返回的臨時unique_ptr很快被銷毀(因為由函數調用而返回的臨時對象在堆中使用完後會被銷毀),沒有機會使用它來訪問無效的數據。換句話說,沒有理由禁止這種賦值。神奇的是,編譯器(GUN GCC g++編譯器支持這種特性)確實允許這種賦值!

總之,程序試圖將一個unique_ptr賦給另一個時,如果源unique_ptr是個臨時右值,編譯器允許這樣做;如果源unique_ptr將存在一段時間,編譯器將禁止這樣做:

using std::unique_ptr;
using std::string;
unique_ptr pu1(new string("Hi ho!"));
unique_ptr pu2;
pu2 = pu1;                                      //#not allowed
unique_ptr pu3;
pu3 = unique_ptr(new string("Yo!"));   //#allowed

語句#1將留下懸掛的unique_ptr(pu1),這句可能導致危害。語句#2不會留下懸掛的unique_ptr,因為它調用unique_ptr的構造函數,該構造函數創建的臨時對象在其所有權轉讓給pu後就被銷毀。這種情況而異的行為表明,unique_ptr優於允許兩種賦值的auto_ptr。

這也是禁止(只是一種建議,編譯器並不禁止)在容器對象中使用auto_ptr,但允許使用unique_ptr的原因。如果容器算法視圖對包含unique_ptr的容器執行類似於語句#1的操作,將導致編譯錯誤;如果算法視圖執行類似於語句#2的操作,則不會有任何問題。而對auto_ptr,類似於語句#1的操作可能導致不確定的行為和神秘崩潰。

相比於auto_ptr,unique_ptr還有另一個優點,它有一個[]用於數組的變體。別忘了,必須將delete和new別對,將delete[]和new[]配對。模板auto_ptr使用delete而不是delete[],因此只能與new一起使用,而不能與new[]一起使用。但unique_ptr有使用new[]和delete[]的版本:

std::unique_ptr pda (new double[5]);  //將使用delete[ ]

3.shared_ptr

shared_ptr模板類摘要:

template
class shared_ptr
{
public:
       typedef T element_type;

       shared_ptr();
       template explicit shared_ptr(Y *p);
       template shared_ptr(Y *p, D d);
       ~shared_ptr();

       shared_ptr( shared_ptr const & r);
       template explicit  shared_ptr(std::auto_ptr & r);        

       shared_ptr &operator=(shared_ptr const & r);
       template shared_ptr &operator=(shared_ptr const &r);
       template shared_ptr &operator=(std::auto_ptr & r);

       void reset( );
       template void reset(Y * p);
       template void reset( Y * p, D d);

       T & operator*( )const;
       T * operator->( ) const;
       T * get( ) const;

       bool unqiue( ) const;
       long use_count( ) const;

       operator unspecified-bool-type( ) const;
       void swap(shared_ptr & b);
}

shared_ptr可以被安全的共享——shared_ptr是一個“全功能”的類,有著正常的拷貝、賦值語義,也可以進行shared_ptr間的比較,是“最智能”的智能指針。

shared_ptr函數介紹:

無參的shared_ptr( )創建一個持有空指針的shared_ptr; shared_ptr(Y *p)獲得指向類型T的指針p的管理權,同時引用計數置為1。這個構造函數要求Y類型必須能夠轉換為T類型; shared_ptr(shared_ptr const & r)從另外一個shared_ptr獲得指針的管理權,同時引用計數加1,結果是兩個shared_ptr共享一個指針的管理權; shared_ptr(std::auto_ptr & r)從一個auto_ptr獲得指針的管理權,引用計數置為1,同時auto_ptr自動失去管理權; operator=賦值操作符可以從另外一個shared_ptr或auto_ptr獲得指針的管理權,其行為同構造函數; shared_ptr( Y p, D d)行為類似shared_ptr(Y p),但使用參數d指定了析構時的定制刪除器,而不是簡單的delete。 shared_ptr的reset( )函數的作用是將引用計數減1,停止對指針的共享,除非引用計數為0,否則不會發生刪除操作。 shared_ptr有兩個專門的函數檢查引用計數。unique( )在shared_ptr是指針的唯一擁所有者時返回true。use_count( )返回當前指針的引用計數。

shared_ptr中所實現的本質是引用計數(reference counting),也就是說shared_ptr是支持復制的,復制一個shared_ptr的本質是對這個智能指針的引用次數加1,而當這個智能指針的引用次數降低到0的時候,該對象自動被析構

需要特別指出的是,如果shared_ptr所表征的引用關系中出現一個環,那麼環上所述對象的引用次數都肯定不可能減為0那麼也就不會被刪除,為了解決這個問題引入了weak_ptr。

4.weak_ptr

引用知乎上的回答

對weak_ptr起的作用,很多人有自己不同的理解,我理解的weak_ptr和shared_ptr的最大區別在於weak_ptr在指向一個對象的時候不會增加其引用計數,因此你可以用weak_ptr去指向一個對象並且在weak_ptr仍然指向這個對象的時候析構它,此時你再訪問weak_ptr的時候,weak_ptr其實返回的會是一個空的shared_ptr。

實際上,通常shared_ptr內部實現的時候維護的就不是一個引用計數,而是兩個引用計數,一個表示strong reference,也就是用shared_ptr進行復制的時候進行的計數,一個是weak reference,也就是用weak_ptr進行復制的時候的計數。weak_ptr本身並不會增加strong reference的值,而strong reference降低到0,對象被自動析構。

為什麼要采取weak_ptr來解決剛才所述的環狀引用的問題呢?需要注意的是環狀引用的本質矛盾是不能通過任何程序設計語言的方式來打破的,為了解決環狀引用,第一步首先得打破環,也就是得告訴C++,這個環上哪一個引用是最弱的,是可以被打破的,因此在一個環上只要把原來的某一個shared_ptr改成weak_ptr,實質上這個環就可以被打破了,原有的環狀引用帶來的無法析構的問題也就隨之得到了解決。

5.智能指針的選擇

(1)如果程序要使用多個指向同一個對象的指針,應選擇shared_ptr。這樣的情況包括:

有一個指針數組,並使用一些輔助指針來標示特定的元素,如最大的元素和最小的元素; 兩個對象包含都指向第三個對象的指針; STL容器包含指針。很多STL算法都支持復制和賦值操作,這些操作可用於shared_ptr,但不能用於unique_ptr(編譯器發出warning)和auto_ptr(行為不確定)。如果你的編譯器沒有提供shared_ptr,可使用Boost庫提供的shared_ptr。

(2)如果程序不需要多個指向同一個對象的指針,則可使用unique_ptr。如果函數使用new分配內存,並返還指向該內存的指針,將其返回類型聲明為unique_ptr是不錯的選擇。這樣,所有權轉讓給接受返回值的unique_ptr,而該智能指針將負責調用delete。可將unique_ptr存儲到STL容器在那個,只要不調用將一個unique_ptr復制或賦給另一個算法(如sort())

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved