雖然在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不滿足這個要求。