c++ 異常處理 析構函數在從構造函數中拋出後調用




try throw catch (2)

我曾經認為,在C ++中,如果構造函數拋出異常,則不會調用此“部分構造”類的析構函數。

但似乎它在C ++ 11中不再成立

這仍然是事實。 自從C ++ 03以來沒有任何改變(對於某些沒有價值的東西;-))

你認為什麼是真的,但是拋出異常時沒有部分構造的對象

C ++ 03 TC1標准說(強調我的):

部分構建或部分銷毀的對象將為所有完全構造的子對象執行析構函數,也就是說,對於構造函數已完成執行且析構函數尚未開始執行的子對象。

即完成其構造函數的任何對像都將通過執行析構函數而被銷毀。 這是一個很好的簡單規則。

在C ++ 11中基本上也適用相同的規則:只要X(int)返回,對象的“構造函數已經完成執行”,因此它是完全構造的,所以它的析構函數會在適當的時候運行(當它出去時範圍或者例外在施工的某個後期階段被拋出。)從本質上講,它仍然是相同的規則。

委託構造函數的主體在另一個構造函數之後運行,並且可以做額外的工作,但這並不改變對象構造完成的事實,因此它完全構造了。 委託構造函數類似於派生類的構造函數,它在基類的構造函數完成後執行更多代碼。 從某種意義上說,你可以認為你的例子是這樣的:

class X
{
public:
    X(int a)
    {
        cout << "X::X(" << a << ")" << endl;
    }
    ~X()
    {
        cout << "X destructor" << endl;
    }
};

class X_delegating : X
{
public:
    X_delegating() : X(10)
    {
        throw runtime_error("Exception thrown in X::X()");    
    }
};

它不是這樣的,只有一種類型,但它與X(int)構造函數運行時類似,然後在委託構造函數中運行其他代碼,並且如果它拋出X “基類”(不是真的是一個基類)被破壞。

我曾經認為,在C ++中,如果構造函數拋出異常,則不會調用此“部分構造”類的析構函數。

但它似乎已經不再是C ++ 11了:我用g ++編譯了下面的代碼,並且它向控制台輸出“ X destructor ”。 為什麼是這樣?

#include <exception>
#include <iostream>
#include <stdexcept>
using namespace std;

class X
{
public:
    X() : X(10)
    {
        throw runtime_error("Exception thrown in X::X()");    
    }
    X(int a)
    {
        cout << "X::X(" << a << ")" << endl;
    }
    ~X()
    {
        cout << "X destructor" << endl;
    }
};

int main()
{
    try
    {
        X x;
    }
    catch(const exception& e)
    {
        cerr << "*** ERROR: " << e.what() << endl;
    }
}

產量

Standard out:
X::X(10) 
X destructor
Standard error: 
*** ERROR: Exception thrown in X::X()

委託構造器確實是一個引入新的銷毀邏輯的新功能。

讓我們重新審視一個對象的生命週期:一個對象的生命週期在一些構造函數完成時開始。 (見15.2 / 2,標準稱這是“主要構造函數”)。就你而言,這是構造函數X(int) 。 第二個委託構造函數X()現在只是一個普通的成員函數。 範圍展開後,將調用所有完全構造對象的析構函數,並且這包括x

這其實意味深長:現在,您可以將“複雜”工作負載放入構造函數中,並充分利用通常的異常傳播,只要您將構造函數委託給另一個構造函數即可。 這樣的設計可以避免需要各種“初始化”功能,而這種功能在過去不需要將太多工作投入到常規構造函數中時就很流行。

定義您所看到的行為的特定語言是:

[C++11: 15.2/2]: [..]同樣,如果一個對象的非委託構造函數已經完成執行,並且該對象的委託構造函數以異常退出,則會調用該對象的析構函數。 [..]





c++11