c++自動釋放的指標之——auto_ptr和shared_ptr
假設我們使用一個用來塑模投資行為(例如股票、債券等)的程式庫,其中各式各樣的投資型別整合自一個root class Investment:
class Investment { ... };//"投資型別"整合體系中的root class
進一步假設,這個程式庫通過一個工廠模式供應我們某特定的Investment物件:
Investment* createInvestment();
//返回指標,指向Investment繼承體系內的動態分配物件。呼叫者有責任刪除它,這裡為了簡化,刻意不寫引數。
createInvesment的呼叫端使用了函式返回的物件後,有責任刪除之,現在考慮有個f函式履行了這個責任:
void f()
{
Investment* pInv = createInvesment();//呼叫factory函式
。。。
delete pInv;
}
這看起來妥當,但若干情況下f可能無法刪除pInv這個指標由於函式之中有某些return語句直接結束了這個函式,那麼delete就不會發生,之後我們洩露的不只是內含投資物件的那塊記憶體,還包括那些投資物件所儲存的任何資源。
為確保createInvestment返回的資源總是被釋放,我們需要將資源放進物件內,當控制流離開f,該物件的解構函式會自動釋放那些資源。把資源放進物件內,我們便可依賴c++的“解構函式自動呼叫機制”確保資源被釋放。標準程式庫提供的auto_ptr正是針對這種形式而設計的特製產品。auto_ptr是個“類指標物件
void f()
{
std::auto_ptr<Investment> pInv(createInvestment());
...
//呼叫factory函式,一如既往的使用pInv,經由auto_ptr的解構函式自動刪除pInv
}
這簡單的例子示範“以物件管理資源”的兩個關鍵想法:
1、獲得資源後獲得立即放進管理物件內,即上述使用的auto_ptr指標,自動釋放。
2、管理物件運用解構函式確保資源被釋放,即自己實現一個類似auto_ptr指標的物件,在裡面運用實現析構來delete
由於auto_ptr被銷燬時會自動刪除它所指之物,所以一定要注意別讓多個auto_ptr同時指向同一物件。如果真是那樣,物件會被刪除一次以上,為了預防這個問題,auto_ptr有一個不同尋常的特質:
若通過copy建構函式或copy assignment操作符複製它們,它們會變成null,而複製所得的指標將取得資源的唯一擁有權!
std::auto_ptr<Investment> pInv1(createInvestment());//pInv1指向物件
std::auto_ptr<Investment> pInv2(pInv1);//pInv2指向物件,pInv1指向null
pInv1 = pInv2;pInv1指向物件,pInv2指向null
auto_ptr的代替方案是“引用技術型智慧指標”tr1::shared_ptr,它能持續追蹤共有多少個物件指向某筆資源,並在無人指向它時自動刪除該資源。
void f()
{
...
std::trl::shared_ptr<Investment> pInv(createInvestment());
//呼叫factory函式,使用pInv一如既往,經由shared_ptr解構函式自動刪除pInv
...
}
這段程式碼看起來和auto_ptr那個版本相同,但shared_ptr的複製行為確實正常的,它不會因為複製而使原來的指標指向null。
問題:
auto_ptr和tr1::shared_ptr兩者都在其析構中做delete而不是delete[],那意味在動態分配而得的array身上使用auto_ptr和tr1::shared_ptr是個餿主意。儘管如此,可嘆的是,編譯器仍能夠通過編譯:
std::auto_ptr<std::string> aps(new std::string[10]);//會用錯誤的delete形式
std::tr1::shred_ptr<int> spi(new int[1024]);//同上
綜上所述:
如果打算手工釋放資源(例如使用delete而非使用一個資源管理類),容易發生某些錯誤。灌裝式的資源管理類如auto_ptr和tr1::shared_ptr往往比較輕鬆的遵循上面的忠告,但是這又存在著別的問題,所以就需要精巧的設計你自己的資源管理類。那並不是很困難。最後需要強調的是——咱們要學會“以物件管理資源”!!!