條款13:以對象管理資源
1、借助析構函數釋放資源
看一個簡單的例子
class Product
{
public:
enum {CPU, KEY};
};
class CPU : public Product {};
class KEY : public Product {};
Product *GetProduct(const int type)
{
switch (type)
{
case Product::CPU: return new CPU();
case Product::KEY: return new KEY();
default: return NULL;
}
}
調用
void call()
{
Product *p = GetProduct(1);
//operator
delete p;
}
GetProduct()獲得的對象資源是需要自己手工釋放的,後面也調用了delete,一切都是循序漸進,假如有以下情況則delete不能執行
①在對象操作中抛出異常
②在delete之前執行了return、goto語句
③GetProduct()在循環裡面執行,并執行continue語句退出 為了確定GetProduct()獲得的資源能夠總是被釋放,做法是将資源放進對象内,在析構函數中執行釋放資源動作,當對象離開控制域後,會自動執行析構函數.
2、智能指針
智能指針實際是一種類對象指針,實作原理是将資源放到對象裡面,在構造函數中擷取對象資源并初始化,在析構函數中delete掉該資源。
auto_ptr 通過auto_ptr修改上面調用
void call()
{
std::auto_ptr<Product> ptr(GetProduct(1)); //将資源放到智能指針中
//operator
} //離開作用域,自動delete掉資源
以對象管理資源的兩個關鍵點 獲得資源後立刻放進管理對象内
實際上“以對象管理資源”的觀念常被稱為“資源取得時機便是初始化時機”,因為我們幾乎總是在獲得一筆資源後于同一語句内以它初始化某個管理對象。有時候獲得的資源被拿來指派(而非初始化)某個管理對象,但不論哪一種做法,每一筆資源都在獲得的同時立刻被放進管理對象中。
管理對象運用析構函數確定資源被釋放
不論控制流如何離開區塊,一旦對象被銷毀(例如當對象離開作用域)其析構函數自然會被自動調用,于是資源被釋放。如果資源釋放動作可能導緻抛出異常,事情變得有點棘手,但條款8已經能夠解決這個問題,是以這裡我們就不多操心了。 auto_ptr被銷毀會自動删除它所指之物,如果用多個auto_ptr指向同一個對象,則多個對象會被删除多次,為了防止這種情 況,auto_ptr有個實作機制,若通過copy函數複制他們,他們就會變為null,而複制所得的指針将得到資源的唯一擁有權。
std::auto_ptr<Product> ptr(GetProduct(1)); //将資源放到智能指針中
std::auto_ptr<Product> ptr2(ptr); //ptr2指向對象,ptr為null
ptr = ptr2; //ptr指向對象,ptr2為null
RCSP智能指針---shared_ptr RCSP(reference-counting smart pointer),是一種引用計數智能指針,追蹤多個對象指向資源,在無人使用時自動釋放資源。 将上面的代碼修改如下
void call()
{
std::tr1::shared_ptr<Product> ptr(GetProduct(1)); //将資源放到智能指針中,引用計數為1
std::tr1::shared_ptr<Product> ptr2(ptr); //ptr2指向對象,引用計數為2
ptr = ptr2; //無變化
} //跳出作用域,引用計數為0,自動執行對象析構函數釋放資源
auto_ptr和shared_ptr兩者在底層析構函數的實作都是通過delete而不是delete[],是以把數組應用到兩者中會出出問題。不過對于數組,可以通過string和vector實作。 對于有些系統資源不是通過new和delete來管理的,我們可以自己實作對象管理類,後面的條款會說到。
記住 1、為防止資源洩漏,請使用RAII對象,它們在構造函數中獲得資源并在析構函數中釋放資源。
2、兩個常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是較佳選擇,因為其copy行為比較直覺。若選擇auto_ptr,複制動作會使它(被複制物)指向null。