Destructor called after throwing from a constructor

C++ExceptionC++11

C++ Problem Overview


I used to think that in C++, if a constructor throws an exception, the destructor of this "partially constructed" class is not called.

But it seems that it is not true anymore in C++11: I compiled the following code with g++ and it prints "X destructor" to the console. Why is this?

#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;
    }
}

Output

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

C++ Solutions


Solution 1 - C++

Delegating constuctors are indeed a new feature that introduces a new destruction logic.

Let us revisit the lifetime of an object: An object's lifetime begins when some constructor has finished. (See 15.2/2. The standard calls this the "principal constructor".) In your case, this is the constructor X(int). The second, delegating constructor X() acts as just a plain member function now. Upon scope unwinding, the destructors of all fully-constructed objects are called, and this includes x.

The implications of this are actually quite profound: You can now put "complex" work loads into a constructor and take full advantage of the usual exception propagation, as long as you make your constructor delegate to another constructor. Such a design can obviate the need for various "init"-functions that used to be popular whenever it wasn't desired to put too much work into a regular constructor.

The specific language that defines the behaviour you're seeing is:

> [C++11: 15.2/2]: [..] Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. [..]

Solution 2 - C++

> I used to think that in C++, if a constructor throws an exception, the destructor of this "partially constructed" class is not called. > > But it seems that it is not true anymore in C++11

It's still true. Nothing changed since C++03 (for some value of nothing ;-) )

What you thought is still true, but there is no partially constructed object when the exception is thrown.

The C++03 TC1 standard says (emphasis mine):

> An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the constructor has completed execution and the destructor has not yet begun execution.

i.e. Any object which has completed its constructor will get destroyed by executing the destructor. That's a nice simple rule.

Fundamentally the same rule applies in C++11: as soon as X(int) has returned, the object's "constructor has completed execution" so it is fully constructed, and so its destructor will run at the appropriate time (when it goes out of scope or an exception is thrown during some later stage of its construction.) It's still the same rule, in essence.

The delegating constructor's body runs after the other constructor and can do extra work, but that doesn't change the fact the object's construction has finished, so it is completely constructed. The delegating constructor is analogous to a derived class' constructor, which executes more code after a base class' constructor finishes. In some sense you can consider your example to be like this:

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()");    
    }
};

it's not really like this, there's only one type, but it's analogous in as much as the X(int) constructor runs, then additional code in the delegating constructor runs, and if that throws the X "base class" (which isn't really a base class) gets destroyed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMr.C64View Question on Stackoverflow
Solution 1 - C++Kerrek SBView Answer on Stackoverflow
Solution 2 - C++Jonathan WakelyView Answer on Stackoverflow