auto_ptr 是C++标準庫提供的類模闆,auto_ptr對象通過初始化指向由new建立的動态記憶體,它是這塊記憶體的擁有者,一塊記憶體不能同時被分給兩個擁有者。當auto_ptr對象生命周期結束時,其析構函數會将auto_ptr對象擁有的動态記憶體自動釋放。即使發生異常,通過異常的棧展開過程也能将動态記憶體釋放。auto_ptr不支援new 數組。
#include <memory>
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 不再被用來指向該對象。
通常的指針在定義的時候若不指向任何對象,我們用Null給其指派。對于智能指針,因為構造函數有預設值0,我們可以直接定義空的auto_ptr如下:
auto_ptr< int > p_auto_int; //不指向任何對象
因為auto_ptr的所有權獨有,是以下面的代碼會造成混亂。
int* p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
因為ap1與ap2都認為指針p是歸它管的,在析構時都試圖删除p, 兩次删除同一個對象的行為在C++标準中是未定義的。是以我們必須防止這樣使用auto_ptr。
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是智能指針作為參數傳遞的底線。
原因很簡單,delete 表達式會被應用在不是動态配置設定的指針上這将導緻未定義的程式行為。
1) get()
傳回auto_ptr指向的那個對象的記憶體位址。如下例:
cout << "the adress of p: "<< p << endl;
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()允許。如下例:
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_auto2( pstr_auto.get() ); //這是兩個auto_ptr擁有同一個對象
auto_ptr< string > pstr_auto2( pstr_auto.release() ); //release可以首先釋放所有權