2010-03-20 89 views
1

在下面的例子中資源獲取就是初始化「RAII」

class X 
{ 
    int *r; 
public: 
    X() { 
     cout << "X is created"; 
     r = new int[10]; 
    }; 
    ~X() { 
     cout<< "X is destroyed"; 
     delete [] r; 
    }; 
}; 
class Y 
{ 
public: 
    Y() { 
     X x; 
     throw 44; 
    }; 
    ~Y() { 
     cout << "Y is destroyed"; 
    }; 
}; 

我從一個站點有RAII的這個例子和AVE有些疑惑。請幫忙。

  1. 在x的構造函數中,我們沒有考慮「如果內存分配失敗」的情況。
  2. 這裏的Y的析構函數是安全的,因爲在y構造函數沒有分配任何內存。如果我們需要在y構造函數中做一些內存分配呢?
+0

請問您可以使用重新格式化代碼嗎?將其分成單獨的行,用間距選擇它,然後單擊代碼示例按鈕(大引號旁邊的1和0)。 – 2010-03-20 17:31:22

+0

你如何定義「考慮」?如果分配失敗,它會拋出,所以在任何情況下都不會將「X」置於半包狀態。 – 2010-03-20 17:32:28

+1

我修復了你的代碼。一般來說,如果你發佈的代碼甚至不是有效的C++,那麼得到答案將會變得更加困難。 – 2010-03-20 17:35:54

回答

8

在x的構造函數中我們沒有考慮「如果內存分配失敗」的情況。

你不需要。如果失敗,構造函數將拋出std::bad_alloc。即:

  1. Ÿ構造函數被調用。
  2. X構造函數被調用。
  3. new int[]分配失敗,拋出std::bad_alloc。內存永遠不會被分配。
  4. 由於X從未完成構造,所以Y構造函數失敗,並且Y從未完成構造。

因此沒有泄漏。

這裏Y的析構函數是安全的,因爲Y中construcotr沒有分配任何內存。如果我們需要在y構造函數中做一些內存分配呢?

你仍然沒有問題。分配失敗將拋出std::bad_alloc。失敗是使用你班級的人的責任。

  1. Ÿ構造函數被調用。
  2. X構造函數被調用。
  3. new int[]分配成功。
  4. Y構造函數現在失敗了,需要拋出異常(例如分配失敗)。
  5. 異常的投擲機構解開調用棧和任何局部變量調用析構函數,在這種情況下,包括十
  6. X的析構函數delete[] S中new int[]

再次,沒有資源在這裏泄漏。

請注意,您確實需要警惕多個分配。即:

class Foo 
{ 
    int * r; 
public: 
    Foo() { 
     r = new int; 
     throw myException; 
    }; 
    ~Foo() { 
     delete r; 
    }; 
}; 

現在我們有一個資源泄漏。當從構造函數拋出異常時,該對象從未完全構造。由於它從來沒有完全構建,它永遠不會有它的析構函數調用。因此我們泄漏r

9

X的構造函數中,如果new失敗,則拋出異常(std::bad_alloc)。這意味着構造函數永遠不會完成,所以對象的生命週期永遠不會啓動,因此它的析構函數永遠不會被調用(沒有對象),並且在new[]delete[]之間沒有不匹配。 (X應該有一個用戶聲明的拷貝構造函數和一個用戶聲明的拷貝賦值操作符,因爲如果構建成功並且該對象被複制或分配,將會違反該保證)。

如果它分配內存在它的構造函數中,這個分配是成功的,那麼如果構造函數的其餘部分在任何時候拋出一個異常,並且如果構造函數完成了內存在析構函數中被釋放,那麼它需要確保釋放這個內存(假設內存是旨在持續對象的生命週期的長度)。

爲了使這個更容易,任何分配的內存應該立即交給一個對象,其唯一的責任是釋放內存。讓一個類管理指向多個分配內存塊的原始指針是複雜且容易出錯的管理代碼的祕訣。

+0

+1這個優秀的點*「X應該有一個用戶聲明的複製構造函數和一個用戶聲明的複製賦值運算符作爲實現提供的將違反此保證,如果構建成功,並且該對象被複制或分配。」* – Nawaz 2011-02-16 08:06:39