【C++智慧指標 auto_ptr】

weixin_34162629發表於2015-08-08

《More Effective C++》ITEM M9他提到auto_ptr。說是當異常產生的時候。怎麼釋放為物件分配的堆記憶體,避免反覆編寫記憶體釋放語句。

PS:這裡書裡面提到函式退出問題,函式退出會清理棧記憶體,無論是怎麼正常退出還是異常退出(僅有一種例外就是當你呼叫 longjmp 時。Longjmp 的這個缺點是 C++領先支援異常處理的主要原因)。建立在此基礎上我們才把對指標的刪除操作封裝到一個棧物件裡面。

這樣函式退出(異常或是正常)就會呼叫物件的解構函式,達到我們自己主動清理所封裝指標指向的記憶體的目的。

作為新手。不是非常理解,記下來,學習學習。

PS:C++11已經不提倡用auto_ptr了,請看連結:http://www.cplusplus.com/reference/memory/auto_ptr/

Note: This class template is deprecated as of C++11. unique_ptr is a new facility with a similar functionality, but with improved security (no fake copy assignments), added features (deleters) and support for arrays. Seeunique_ptr for additional information.


下面內容copy自:http://blog.sina.com.cn/s/blog_7708265a01010lyv.html

1. auto_ptr是什麼?

auto_ptr 是C++標準庫提供的類别範本,auto_ptr物件通過初始化指向由new建立的動態記憶體,它是這塊記憶體的擁有者。一塊記憶體不能同一時候被分給兩個擁有者。

當auto_ptr物件生命週期結束時,其解構函式會將auto_ptr物件擁有的動態記憶體自己主動釋放。即使發生異常。通過異常的棧展開過程也能將動態記憶體釋放。auto_ptr不支援new 陣列。

 

2. auto_ptr須要包括的標頭檔案?

#include <memory>

 

3. 初始化auto_ptr物件的方法?

1) 建構函式

1] 將已存在的指向動態記憶體的普通指標作為引數來構造

int* p = new int(33);

auto_ptr<int> api(p);

2] 直接構造智慧指標

auto_ptr< int > api( new int( 33 ) );

2) 拷貝構造

利用已經存在的智慧指標來構造新的智慧指標

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2( pstr_auto );  //利用pstr_auto來構造pstr_auto2

由於一塊動態記憶體智慧由一個智慧指標獨享,所以在拷貝構造或賦值時都會發生擁有權轉移的過程。在此拷貝構造過程中,pstr_auto將失去對字串記憶體的全部權,而pstr_auto2將其獲得。物件銷燬時,pstr_auto2負責記憶體的自己主動銷燬。

3) 賦值

利用已經存在的智慧指標來構造新的智慧指標

auto_ptr< int > p1( new int( 1024 ) );

auto_ptr< int > p2( new int( 2048 ) );

p1 = p2;

在賦值之前,由p1 指向的物件被刪除。賦值之後,p1 擁有int 型物件的全部權。

該物件值為2048。 p2 不再被用來指向該物件。

 

4. 空的auto_ptr 須要初始化嗎?

通常的指標在定義的時候若不指向不論什麼物件。我們用Null給其賦值。對於智慧指標。由於建構函式有預設值0。我們能夠直接定義空的auto_ptr例如以下:

auto_ptr< int > p_auto_int;  //不指向不論什麼物件

 

5. 防止兩個auto_ptr物件擁有同一個物件(一塊記憶體)

由於auto_ptr的全部權獨有。所以以下的程式碼會造成混亂。

int* p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);

由於ap1與ap2都覺得指標p是歸它管的。在析構時都試圖刪除p, 兩次刪除同一個物件的行為在C++標準中是沒有定義的。所以我們必須防止這樣使用auto_ptr。

 

6. 警惕智慧指標作為引數!

1) 按值傳遞時,函式呼叫過程中在函式的作用域中會產生一個區域性物件來接收傳入的auto_ptr(拷貝構造),這樣,傳入的實參auto_ptr就失去了其對原物件的全部權,而該物件會在函式退出時被區域性auto_ptr刪除。

例如以下例:

void f(auto_ptr<int> ap)

{cout<<*ap;}
auto_ptr<int> ap1(new int(0));
f(ap1);
cout<<*ap1; //錯誤,經過f(ap1)函式呼叫,ap1已經不再擁有不論什麼物件了。

2) 引用或指標時,不會存在上面的拷貝過程。但我們並不知道在函式中對傳入的auto_ptr做了什麼,假設其中某些操作使其失去了對物件的全部權,那麼這還是可能會導致致命的執行期錯誤。

結論:const reference是智慧指標作為引數傳遞的底線。

 

7. auto_ptr不能初始化為指向非動態記憶體

原因非常easy,delete 表示式會被應用在不是動態分配的指標上這將導致沒有定義的程式行為。

 

8. auto_ptr經常使用的成員函式

1) get()

返回auto_ptr指向的那個物件的記憶體地址。例如以下例:

int* p = new int(33);

cout << "the adress of p: "<< p << endl;

auto_ptr<int> ap1(p);

cout << "the adress of ap1: " << &ap1 << endl;

cout << "the adress of the object which ap1 point to: " << ap1.get() << endl;

輸出例如以下:

the adress of p: 00481E00

the adress of ap1: 0012FF68

the adress of the object which ap1 point to: 00481E00

第一行與第三行同樣,都是int所在的那塊記憶體的地址。

第二行是ap1這個類物件本身所在記憶體的地址。

2) reset()

又一次設定auto_ptr指向的物件。類似於賦值操作,但賦值操作不同意將一個普通指標指直接賦給auto_ptr,而reset()同意。例如以下例:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

pstr_auto.reset( new string( "Long -neck" ) );

在樣例中,重置前pstr_auto擁有"Brontosaurus"字元記憶體的全部權。這塊記憶體首先會被釋放。之後pstr_auto再擁有"Long -neck"字元記憶體的全部權。

注:reset(0)能夠釋放物件,銷燬記憶體。

3) release()

返回auto_ptr指向的那個物件的記憶體地址,並釋放對這個物件的全部權。

用此函式初始化auto_ptr時能夠避免兩個auto_ptr物件擁有同一個物件的情況(與get函式相比)。

樣例例如以下:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2( pstr_auto.get() ); //這是兩個auto_ptr擁有同一個物件

auto_ptr< string > pstr_auto2( pstr_auto.release() ); //release能夠首先釋放全部權

附上auto_ptr的實現程式碼:

namespace std
{
 template<class T>
 class auto_ptr 
 {
 private:
  T* ap; 
 public:

  // constructor & destructor ----------------------------------- (1)
  explicit auto_ptr (T* ptr = 0) throw() : ap(ptr){}

  ~auto_ptr() throw() 
  {
   delete ap;
  }

  
  // Copy & assignment --------------------------------------------(2)
  auto_ptr (auto_ptr& rhs) throw() :ap(rhs.release()) {}
  template<class Y>  
  auto_ptr (auto_ptr<Y>& rhs) throw() : ap(rhs.release()) { }

  auto_ptr& operator= (auto_ptr& rhs) throw() 
  {
   reset(rhs.release());
   return *this;
  }
  template<class Y>
  auto_ptr& operator= (auto_ptr<Y>& rhs) throw() 
  {
   reset(rhs.release());
   return *this;
  }

  // Dereference----------------------------------------------------(3)
  T& operator*() const throw() 
  {
   return *ap;
  }
  T* operator->() const throw() 
  {
   return ap;
  }

  // Helper functions------------------------------------------------(4)
  // value access
  T* get() const throw() 
  {
   return ap;
  }

  // release ownership
  T* release() throw()
  {
   T* tmp(ap);
   ap = 0;
   return tmp;
  }

  // reset value
  void reset (T* ptr=0) throw() 
  {
   if (ap != ptr) 
   {
    delete ap;
    ap = ptr;
   }
  }

  // Special conversions-----------------------------------------------(5)
  template<class Y>
  struct auto_ptr_ref
  {
   Y* yp;
   auto_ptr_ref (Y* rhs) : yp(rhs) {}
  };

  auto_ptr(auto_ptr_ref<T> rhs) throw() : ap(rhs.yp) { }
  auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() 
  {  
   reset(rhs.yp);
   return *this;
  }
  template<class Y>
  operator auto_ptr_ref<Y>() throw() 
  {
   return auto_ptr_ref<Y>(release());
  }
  template<class Y>
  operator auto_ptr<Y>() throw()
  {
   return auto_ptr<Y>(release());
  }
 };
}


版權宣告:本文部落格原創文章,部落格,未經同意,不得轉載。

相關文章