从构造函数抛出后调用析构函数

Mr.*_*C64 73 c++ exception c++11

我曾经认为在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

产量

Standard out:
X::X(10) 
X destructor
Standard error: 
*** ERROR: Exception thrown in X::X()
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 81

委托构造函数确实是一个引入新破坏逻辑的新功能.

让我们重温一生的对象:当一个对象的生命周期开始一些构造已经完成.(见15.2/2.标准称之为"主要构造函数".)在您的情况下,这是构造函数X(int).第二个,委托构造X()函数现在只是一个普通的成员函数.在范围展开时,将调用所有完全构造的对象的析构函数,这包括x.

这样做的含义实际上非常深刻:您现在可以将"复杂"工作负载放入构造函数中,并充分利用通常的异常传播,只要您将构造函数委托给另一个构造函数即可.这样的设计可以消除对各种"初始化"功能的需要,这些功能在不希望将大量工作投入常规构造函数时曾经普及.

定义您所看到的行为的特定语言是:

[C++11: 15.2/2]: [..]类似地,如果对象的非委托构造函数已完成执行并且该对象的委托构造函数以异常退出,则将调用该对象的析构函数.[..]

  • 使用`[C++ 11:15.2/2]` (8认同)
  • @Nawaz:"基本概念 - 对象生命周期"? (2认同)

Jon*_*ely 26

我曾经认为在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()");    
    }
};
Run Code Online (Sandbox Code Playgroud)

它不是真的这样,只有一种类型,但它与X(int)构造函数运行的类似,然后委托构造函数中的其他代码运行,如果抛出X"基类"(它实际上不是基类)被摧毁.

  • 奇怪的是这个答案只有一个upvote.虽然Kerrek提到为什么存在这个功能,但我认为这个功能可以更好地回答实际问题. (3认同)