1. 程式人生 > >【effective C++】13、以物件來管理資源

【effective C++】13、以物件來管理資源

1、物件管理資源的必要性

模擬投資行為的程式庫:

class Investment{···};    //投資繼承體系中的root class

//程式庫利用工廠函式供應給我們特定的Investment物件:
//這裡返回的指標指向Investment繼承體系內的動態分配物件,呼叫者有責任最後刪除它
Investment* createInvestment();

void f(){
    Investment* pInv = createInvestment();
    ···
    delete pInv;
}

上面的例子看起來嚴謹,其實由一個問題,就是在“…“中程式如果正常或者非正常的終止,控制流不會觸及到delete語句,這時就會發生記憶體洩漏,包括投資物件所儲存的所有資源。
為了確保資源總是被釋放,需要把資源放到物件內,當控制流離開f函式,物件的解構函式會自動的釋放資源:把資源放進物件內,便可以依賴C++的解構函式自動呼叫機制來確保資源被釋放。

2、物件資源管理方法

資源多被動態分配於heap中,被用於區塊或者函式中。而資源需要在離開區塊或函式的時候被釋放。
標準程式庫提供了auto_ptr,它是一個類指標物件,也就是所謂的“智慧指標“,解構函式自動對其所指向的物件呼叫delete。

void f(){
    //呼叫factory函式,一如既往的使用pInv,出函式時由auto_ptr的解構函式自動刪除pInv
    std::auto_ptr<Investment> pInv(createInvestment());
    ···
}

用物件管理資源的兩個關鍵想法:
獲得資源後立即放進管理物件內:

程式碼中createInvestment返回的資源被當作auto_ptr的初始值,實際觀念就是資源的取得時機便是初始化的時機,每一次資源在獲得的同時被放進管理物件中。

管理物件利用解構函式確保資源被釋放:無論控制流如何離開區塊或函式,一旦物件pInv被銷燬,解構函式會被自動呼叫,資源被釋放。如果資源釋放過程中丟擲異常,回到條款8中解構函式中丟擲異常的處理。

3、管理資源的物件的注意事項

別讓多個auto_ptr指向同一個物件::這種情況下同一個資源或被刪除一次以上,程式會出現“未定義的行為“。而auto_ptr有一個特別的性質:auto_ptr所指向的物件,通過copy建構函式或者copy assignment操作符複製它們,它們會變成null,複製得到的指標將取得資源的唯一擁有權。

std::auto_ptr<Investment> pInv1(createInvestment());

//這時pInv2指向物件,pInv1被設為null
std::auto_ptr<Investment> pInv2(pInv1);

//這時pInv1指向物件,pInv2被設為null
pInv1 = pInv2;

auto_ptr並非管理動態分配資源的神兵利器,如STL容器正常的複製行為不能用auto_ptr.

引用計數型的智慧指標:持續追蹤共有多上個物件指向某資源,無人指向他的時候自動刪除資源。tr1::share_ptr就是這種計數型的智慧指標。

std::tr1::share_ptr<Investment> pInv1(createInvestment());

//這時pInv1和pInv2指向同一個物件
std::tr1::share_ptr<Investment> pInv2(pInv1);

//這時pInv1和pInv2指向同一個物件,沒有發生任何改變
pInv1 = pInv2;

auto_ptr和tr1::share_ptr兩者在解構函式中呼叫delete而不是delete[],動態分配得到的資源不能由它們來指向:

std::auto_ptr<std::string> aps(new std::string[10]);

std::tr1::share_ptr<int> spi(new int[1204]);

總結

  • 為了防止記憶體洩漏,請用物件來管理資源,它們在建構函式中獲取資源並在解構函式中釋放資源。
  • 兩個常用的智慧型指標auto_ptr和tr1::share_ptr,後者是一個比較好的選擇,複製行為比較直觀,注意前者的複製會時原來的指標指向null。