資源是,一旦用了,將來必須還給系統,除了記憶體常見的還有檔案描述符(file description)、互斥鎖、圖形介面中的筆刷、資料庫連接
以及網路socket。考慮到異常、函數內多重回傳路徑、程式維護員軟體改動卻沒有理解隨之而來的衝擊,發現資源管理的特殊手段還不很充分夠用。
假設一個各式各樣的投資類型繼承的一個root class Investment:
class Investment {...};
假設通過一個工廠函數供應我們某特定的Investment對象:
Investment* createInvestment();//返回指向Investment繼承體系的動態指派至。調用者有責任刪除它。
void f()
{
Investment* pInv = createInvestment(); //調用factory函數
...
delete pInv; //釋放pInv所指對象
}
若果…中有一個過早的return語句,或者拋出異常,那麼控制流程將不會幸臨delete。
為確保資源總是被釋放,我們需要將資源放進對象內,這樣便可依賴c++的“解構函式自動調用機制”確保資源被釋放。標準程式庫提供的auto_ptr正是針對這種形勢設計的特製產品。auto_ptr是一個“類指標(pointer-like)對象”:
void f()
{
std::auto_ptr<Investment> pInv(createInvestment());
...
} //經由auto_ptr的解構函式自動刪除pInv
1.獲得資源後立即放進管理對象(managing object)內。createInvestment返回的資源被當做其管理者auto_ptr的初值。
“以對象管理資源”的觀念常被稱為“資源取得時便是初始化時”(resource Acquisition is Initialization;RAII),有時獲得的資源拿來賦值(而非初始化)某個管理對象,但不論哪種做法,每筆資源在獲得的同時立刻被放進管理對象中。
2.管理對象運用解構函式確保資源被釋放。一旦對象被銷毀(當對象離開範圍),其解構函式自然會自動調用,於是資源被釋放。
由於autu_ptr被銷毀時會自動刪除它所指之物,所以一定不要讓多個auto_ptr同時指向同一個對象。為防止這個問題,auto_ptr有一個不尋常的性質:若通過copy建構函式或copy assignment操作符複製它們,它們會變成null,而複製所得的指標取得資源的唯一使用權。
std::auto_ptr<Investment> pInv1(createInvestment());
std::auto_ptr<Investment> pInv2(pInv1);//現在pInv2指向對象,pInv1被設為null
pInv1 = pInv2;//現在pInv1指向對象,pInv2被設為null
這個詭異的複製行為,使得要求其元素髮揮“正常的”複製行為的像stl容器這種容不得auto_ptr。
auto_ptr的替代方案是“引用計數型智慧指標”(reference-counting smart pointer;RCSP)PCSP持續追蹤共有多少對象指向
某筆資源,並在無人指向它時刪除該資源。類似於記憶體回收,但無法打破環狀引用(cycle of reference,例如兩個其實已經沒有被使用的對象彼此互指,因而好像在“被使用”狀態)。
TR1的tr1:shared_ptr就是個RCSP,所以可以這麼寫f:
void f()
{
std::tr1::shared_ptr<Investment> pInv(createInvestment());
...
}//經由shared_ptr的解構函式自動刪除pInv
shared_ptr的複製正常多了
void f()
{
std::tr1::shared_ptr<Investment> pInv1(createInvestment());
std::tr1::shared_ptr<Investment> pInv2(pInv1);//pInv1和pInv2指向同一個對象
pInv1 = pInv2;
...
}//pInv1和pInv2被銷毀,他們指向的對象也就被銷毀。
所以shared_ptr可以被用於stl容器及其他“auto_ptr之非正統複製行為並不合適”的語境上。
auto_ptr及shared_ptr在其解構函式內做delete而不是delete[]動作。那意味著在動態分配而得的array身上使用auto_ptr或shared_ptr是個餿主意。因為vector和string幾乎總是可以替代動態分配而得的數組。boost::scope_array 和boost::shared_array classes是針對數組設計的類似auto_ptr和tr1::shared_ptr那樣的類。
為防止資源泄漏,請使用RAII對象,他們在建構函式中獲得資源並在解構函式中釋放資源。shared_ptr比auto_ptr是個較佳的選擇
因為其copy行為比較直觀。