智能指針auto_ptr



智能指針(auto_ptr) 這個名字聽起來很酷是不是?其實auto_ptr 只C++標準庫提供的一個類模板,它與傳統的new/delete控制內存相比有一定優勢,但也有其侷限。本文總結的8個問題足以涵蓋auto_ptr的大部分內容。

 

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不能初始化爲指向非動態內存

原因很簡單,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可以首先釋放所有權

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