2013-08-28 65 views
5
class XX 
{ 
public: 
    static unsigned s_cnt; 
    XX() 
    { 
     ++ s_cnt; 
     std::cout << "C XX " << s_cnt << "\n"; 

     if (s_cnt > 2) 
      throw std::exception(); 
    } 

    //private: 
    ~XX() 
    { 
     std::cout << "~ XX\n"; 
    } 
}; 
unsigned XX::s_cnt = 0; 

int main() 
{ 
    try 
    { 
     XX *xx = new XX[10]; 
    } catch (...) 
    { 
     std::cout << "Exc\n"; 
    } 
} 

輸出:爲什麼在數組創建過程中當未捕獲的異常拋出C++時調用析構函數?

C XX 1 
C XX 2 
C XX 3 
~ XX 
~ XX 
Exc 

但是,當我刪除的try-catch,我看到:

C XX 1 
C XX 2 
C XX 3 
terminate called after throwing an instance of 'std::exception' 
    what(): std::exception 
zsh: abort  ./a.out 

爲什麼C++調用在第一種情況下的析構函數,但不是在第二?

回答

13

當你不捕捉異常(即它成爲一個未捕獲的異常而終止程序),C++不給你,實際上析構函數被調用的任何保證。

這給出如何實現異常處理的編譯器餘地。例如,GCC首先搜索處理程序。如果找不到,它會立即中止,保存完整的堆棧信息以進行調試。如果它確實找到了它,它實際上會放出堆棧,銷燬對象,直到它到達處理程序。這就是爲什麼你看不到輸出:程序在銷燬任何對象之前中止。

+1

您可能會補充一點,原因是您不會在覈心轉儲中丟失狀態信息。 –

+0

好點,補充。 –

3

當您從構造函數中拋出異常時,該標準將該對象視爲未構造。你不會毀掉那些不存在的東西,不是嗎?

事實上,即使這個簡單的例子不按照你的建議的工作:

struct A { 
    A() {throw std::exception();} 
    ~A() {std::cout << "A::~A()" << std::endl;} 
}; 

int main() { 
    A a; 
} 

這終止與未捕獲到異常,但不打印「A ::〜A()」。

如果你想想看,這是給一個語義它的唯一可能的方式。

例如,我們可以使用我們的類型A作爲B類成員對象:

struct B { 
    B() : m_a(),m_p(new int) {} 
    ~B() {delete m_p;} 

    A m_a; 
    int * m_p; 
}; 

顯然B::B()立即引發(和甚至不初始化m_p)。如果在這種情況下假設的C++標準要求調用B::~B(),則析構函數無法知道m_p是否已被初始化。

換句話說,從構造函數拋出異常意味着該對象從不存在,它的生命週期從未開始。這個GotW很清楚。

獎勵:如果在B的定義中我們交換了m_am_p的訂單會發生什麼? 然後你有內存泄漏。這就是爲什麼在成員對象的初始化過程中存在一種特殊的語法來捕獲異常。

+0

新XX [10]逐個構建對象。前2個對象的構建沒有任何問題。然後它試圖構建第三個失敗。然後它調用XX [1]和XX [0]的析構函數,但不是XX [2]。實際上,在你的輸出中只有2條'〜XX'行,而不是3條。 – sbabbi

+0

我指的是第二種情況,儘管構造了對象,但try塊已被刪除,並且沒有調用析構函數。你沒有解決問題的這一部分。 – juanchopanza

+0

好吧,我錯過了修改問題標題的編輯,我的錯誤。第二部分由@Sebastian Redl回答。但它與您使用數組的事實無關。 – sbabbi

相關問題