C++ 智能指針 std::auto_ptr 分析


背景介紹:

  RAll機制

  定義一個類來封裝資源的分配和釋放,在構造函數中完成資源的分配和初始化,在析構函數中完成資源的清理,從而保證資源的正確初始化和清理

ps:智能指針就是RAll機制的一種應用,智能指針從根本上來說是一個對象


1.auto_ptr是什么?

auto_ptr是C++標准庫提供的類模板,auto_ptr對象通過初始化指向由new創建的動態內存,它是這塊內存的擁有者,一塊內存不能同時被分配給兩個擁有者,當auto_ptr對象生命周期結束時,其析構函數會將auto_ptr對象擁有的動態內存自動釋放,即使發生異常,通過異常棧的展開過程也能將動態內存釋放,auto_ptr不支持new 數組

ps:頭文件:#include<memory>


2.初始化auto_ptr的方法

1)構造函數

將已存在的指向動態內存的普通指針作為參數來構造

int *p=new int(33);

auto_ptr<int> aptr(p);

直接構造

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

2)拷貝構造

利用已經存在的智能指針來構造新的智能指針

auto_ptr<string> p1(new string("name"));
auto_ptr<string> p2(p1);

注意:因為一塊動態內存只能由一個智能指針獨享,所以在拷貝構造和賦值時都會發生擁有權轉移,在此拷貝構造過程中,p1將失去對字符串內存的所有權,而p2獲得,對象銷毀時,p2負責內存的自動銷毀

3)賦值

利用已經存在的智能指針來構造新的智能指針

auto_ptr<string> p1(new string("name"));
auto_ptr<string> p2(new string("sex"));

p1=p2;

在賦值之前由p1指向的對象被刪除,賦值之后p1擁有字符串sex的內存所有權,p2不再被用來指向sex字符串對象

ps:可以直接定義一個空的智能指針,默認值為0


3.防止兩個auto_ptr對象擁有同一個對象(同一塊內存)

因為auto_ptr的內存所有權是獨占的,所以以下代碼存在問題

int *p=new int(10);

auto_ptr<string> p1(p);
auto_ptr<string> p2(p);

因為p1和p2都認為p是由它管,在析構時都試圖刪除p,兩次刪除同一個對象的行為在C++標准中是未定義的,我們必須防止這樣使用auto_ptr


4.警惕auto_ptr作為參數

1)按值傳遞時,函數的調用過程中在函數的作用域中會產生一個局部對象來接收傳入的auto_ptr(拷貝構造),這樣傳入的實參就其對原對象的所有權,而該對象在函數退出時會被局部的auto_ptr刪除,樣例如下:

void f(auto_ptr<int> aptr)
{
    cout<<*aptr<<endl;
}
int main()
{
    auto_ptr<int> aptr(new int(10));
    f(aptr);
    cout<<*aptr<<endl;//錯誤,經過f函數調用,原有的aptr已經不再擁有任何對象了

}

2)參數為引用或指針,雖然不會存在上述的拷貝過程,但是我們並不知道函數對傳入的auto_ptr做了什么,如果其中的某種操作使其失去了對象的所有權,那么還是會導致致命的執行錯誤

結論:const reference是auto_ptr作為參數傳遞的底線


5.auto_ptr不能初始化為指向非動態內存

因為delete表達式被應用在指向非動態內存的指針上,這是C++未定義的程序行為


6.auto_ptr常用的成員函數

1)get:返回auto_ptr指向的那個對象的內存地址

int main()
{
    int *p=new int(10);
    cout << "the adress of p: "<< p << endl;

    auto_ptr<int> aptr(p);

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

}

/*
程序運行結果:
the adress of p: 0xb50f80
the adress of aptr: 0x69fed8
the adress of the object which aptr point to: 0xb50f80
*/

2)reset:重新設置auto_ptr指向的對象,類似於賦值操作,但是賦值操作不允許將一個普通指針直接賦值給auto_ptr,而reset允許

reset(0)可以釋放對象,銷毀內存

int main()
{
    auto_ptr<string> aptr(new string("name"));
    aptr.reset(new string("sex"));

    cout<<*(aptr.get())<<endl;
    cout<<*aptr<<endl;
}
/*
程序運行結果:
sex
sex
*/

3)release:返回auto_ptr指向的那個對象的內存地址,並且釋放這個對象的所有權

用此函數初始化auto_ptr可以避免兩個auto_ptr對象指向同一個對象的情況

int main()
{
    auto_ptr<string> aptr(new string("name"));

    auto_ptr<string> aptr1(aptr.get());//這是兩個auto_ptr擁有同一個對象
    auto_ptr<string> aptr2(aptr.release());//release可以先釋放所有權,這樣就不會指向同一個對象
}

該函數不會釋放對象,僅僅會歸還所有權


7.千萬不要把auto_ptr對象放在容器中

容器要求對象可以進行安全的賦值操作,可以將一個對象拷貝到另外一個對象,從而獲得兩個獨立的邏輯上相同的拷貝,尤其是當一個對象被拷貝到另外一個對象后,原來的對象不會改變,但是很顯然,auto_ptr不支持這個性質


使用總結:

1)兩個auto_ptr對象不會同時指向同一個內存,要明白兩個auto_ptr對象賦值會發生什么

2)auto_ptr對象存儲的指針應該為NULL或者指向一個動態內存塊

3)auto_ptr對象存儲的指針應該指向單一物件(是new出來的,而不是new[]出來的,不支持指向指針數組)

4)千萬不要把auto_ptr對象放在容器中

5)當將auto_ptr作為參數傳遞時,const reference是auto_ptr作為參數傳遞的底線


結論:使用一個auto_ptr的限制很多,還不能用來管理堆內存數組,使用不當會引發很多的問題,其中一些設計也不符合C++設計的思想,所以產生了boost的智能指針,boost的智能指針可以解決上面的問題!下節將討論boost智能指針


auto_ptr的本質是管理權限的轉移!



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM