聊聊auto_ptr

雖然在C++11中,auto_ptr已經不推薦使用了,代之以功能類似的unique_ptr。但作爲曾經的一個C++重要存在,還是來簡單瞭解一下吧。

一、auto_ptr簡介

auto_ptr是C++的一個類,它的設計初衷是,動態分配對象以及當對象不再需要時能夠自動進行內存回收,爲動態分配的對象提供異常安全。auto_ptr類定義在memory頭文件中,使用時需要#include <memory>。

智能指針的用法如下:

auto_ptr<T> ap;  // 創建名爲ap的未綁定的auto_ptr對象

auto_ptr<T> ap(p);  //創建名爲ap的auto_ptr對象,ap擁有指針p指向的對象,該構造函數爲explicit

auto_ptr<T> ap1(ap2);  //創建名爲ap1的auto_ptr對象,ap1保存原來存儲在ap2中的指針,ap2將對象所有權轉讓給ap1,ap2成爲未綁定的auto_ptr對象

ap1 = ap2;  //ap2將所有權轉讓給ap1,刪除ap1指向的對象,並使ap1指向原來ap2指向的對象,使ap2成爲未綁定的auto_ptr對象

~ap();   //析構函數,刪除ap指向的對象

*ap   //返回ap所綁定的對象的引用

ap->    //返回ap保存的指針

ap.reset(p);   // 如果p與ap的值不同,則刪除ap所指向的對象,並將ap綁定到p指向的對象

ap.release();  // 返回ap所保存的指針並且使ap成爲未綁定的auto_ptr對象

ap.get();  // 返回ap所保存的指針

智能指針有幾點需要說明:

1. 爲異常安全的內存分配使用auto_ptr

如果通過常規指針進行內存分配,那麼如果在執行delete操作之前發生異常,就不會自動釋放內存。看下面一個例子:

void func()
{
    int *p = new int (100);
    // Do something
    delete p;
}

如果在分配內存和delete p之間發生異常,並且該異常不會在函數中被捕獲,就不會執行delete p,從而導致內存無法回收。而如果使用auto_ptr,則會執行自動內存釋放。

void func()
{
    auto_ptr<int> p(new int (100));
    //Do something
}

2. auto_ptr是可以保存任何類型指針的模板

可以創建任何類型的auto_ptr,類型參數通過auto_ptr後面的尖括號指定,例如:

 auto_ptr<string> sp(new string ("Hello world!"));

3. 將auto_ptr綁定到指針

最常見的情況是,將auto_ptr對象初始化爲由new表達式返回的對象的地址,見上面的例子。接受指針的構造函數爲explicit構造函數,因此必須使用初始化的直接形式來創建auto_ptr對象。

auto_ptr<int> p1 = new int (1024);   //編譯錯誤,構造函數是explicit,而不能通過implicit使用

auto_ptr<int> p1(new int (1024));  //編譯OK

p1所指的由new表達式創建的對象,在超出作用域時自動刪除。如果p1是局部對象,p1所指向的對象在定義p1的程序塊的末尾刪除;如果發生異常,則p1也超出作用域,p1的析構函數將作爲異常處理的一部分而自動執行;如果p1是全局對象,就在程序的末尾刪除p1引用的對象。

4. auto_ptr的複製和賦值是破壞性操作

auto_ptr和普通指針對待複製和賦值有非常關鍵的重要區別。以複製爲例。對於普通指針來說,在複製之後,兩個指針指向同一個對象;而複製auto_ptr對象之後,原來的auto_ptr將基礎對象的所有權轉讓給新的auto_ptr對象,而原來的auto_ptr不再綁定任何基礎對象。

auto_ptr<string> sp1(new string ("This is a string."));  //sp1對字符串擁有所有權

auto_ptr<string> sp2(sp1);  //字符串的所有權由sp1轉移到sp2,sp1不再指向原字符串,也不指向任何字符串對象

與我們所認知的複製或賦值操作不同,auto_ptr的複製和賦值會改變右操作數,因此,賦值的左右操作數都必須是可修改的左值。

auto_ptr<string> sp3(new string ("Happy birthday!"));

sp3 = sp2;

以上操作中,sp3原本指向字符串“Happy birthday!”;使用sp2對sp3賦值後,刪除了sp3原來指向的字符串對象“Happy birthday!”;然後將sp3指向原來sp2指向的字符串對象“This is a string.”;sp2則變成未綁定的auto_ptr。

5. 測試auto_ptr對象使用get()成員

對於普通指針來說,我們可以通過if(p)來判斷指針是否爲空,而auto_ptr則不能這樣直接測試,要測試auto_ptr對象,必須使用它的get成員,該成員返回包含在auto_ptr對象中的基礎指針:

//假定p_auto是auto_ptr<int>類型
if(p_auto.get())
{
    *p_auto = 1000;
}

6. reset操作

auto_ptr與普通指針的另一個區別是,不能直接將一個地址賦值給auto_ptr對象,例如下面的操作是錯誤的:

p_auto = new int (1000);

必須調用reset()來改變指針:

if(p_auto.get())
{
    *p_auto = 1000;
}
else
{
    p_auto.reset(new int (1000));
}

7. auto_ptr對象的使用

auto_ptr類重載了操作符“*”和“->”,因此可以使用類似普通指針的方式使用智能指針。例如:

*sp = "abcdefg";

string str = *sp;

二、auto_ptr的缺陷

auto_ptr類模板爲處理動態內存分配和回收提供了安全性和便利性,但它同時也是把雙刃劍,一不小心就會跌入陷阱。auto_ptr的使用有如下限制,這也是爲什麼C++11不再推薦使用的原因,因爲確實有點反常識。

1. 不能使用auto_ptr對象保存指向靜態分配對象的指針,否則,當auto_ptr對象本身被銷燬的時候,它會試圖去釋放非動態分配的內存,導致未定義行爲;

2. 不能使用兩個auto_ptr指向同一對象,因爲在兩個auto_ptr對象被銷燬時,會導致同一個對象被釋放兩次;

3. auto_ptr只能用於管理從new返回的一個對象,而不能管理動態分配的數組,因爲其釋放對象時使用的是delete操作符,而不是用釋放數組的delete[]操作符。

4. 不能將auto_ptr對象存儲在STL標準容器中,因爲容器要求所保存的類型定義複製和賦值兩種操作,在複製和賦值之後,兩個對象必須具有相同的值,而auto_ptr不滿足這個要求。

 

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