How to handle constructors that must acquire multiple resources in an exception safe manner

Is there a better way?

YES

C++11 delivers a new feature called delegating constructors which deals with this situation very gracefully. But it is a bit subtle.

The problem with throwing exceptions in constructors is to realize that the destructor of the object you're constructing doesn't run until the constructor is complete. Though the destructors of sub objects (bases and members) will run if an exception is thrown, as soon as those sub objects are fully constructed.

The key here is to fully construct X before you start adding resources to it, and then add resources one at a time, keeping the X in a valid state as you add each resource. Once the X is fully constructed, ~X() will clean up any mess as you add resources. Prior to C++11 this might look like:

X x;  // no resources
x.push_back(A(1));  // add a resource
x.push_back(A(2));  // add a resource
// ...

But in C++11 you can write the multi-resource-acquizition constructor like this:

X(const A& x, const A& y)
    : X{}
{
    data_ = static_cast<A*>(::operator new (2*sizeof(A)));
    ::new(data_) A{x};
    ++size_;
    ::new(data_ + 1) A{y};
    ++size_;
}

This is pretty much like writing code completely ignorant of exception safety. The difference is this line:

    : X{}

This says: Construct me a default X. After this construction, *this is fully constructed and if an exception is thrown in subsequent operations, ~X() gets run. This is revolutionary!

Note that in this case, a default-constructed X acquires no resources. Indeed, it is even implicitly noexcept. So that part won't throw. And it sets *this to a valid X that holds an array of size 0. ~X() knows how to deal with that state.

Now add the resource of the uninitialized memory. If that throws, you still have a default constructed X and ~X() correctly deals with that by doing nothing.

Now add the second resource: A constructed copy of x. If that throws, ~X() will still deallocate the data_ buffer, but without running any ~A().

If the second resource succeeds, set the X to a valid state by incrementing size_ which is a noexcept operation. If anything after this throws, ~X() will correctly clean up a buffer of length 1.

Now try the third resource: A constructed copy of y. If that construction throws, ~X() will correctly clean up your buffer of length 1. If it doesn't throw, inform *this that it now owns a buffer of length 2.

Use of this technique does not require X to be default constructible. For example the default constructor could be private. Or you could use some other private constructor that puts X into a resourceless state:

: X{moved_from_tag{}}

In C++11, it is generally a good idea if your X can have a resourceless state as this enables you to have a noexcept move constructor which comes bundled with all kinds of goodness (and is the subject of a different post).

C++11 delegating constructors is a very good (scalable) technique for writing exception safe constructors as long as you have a resource-less state to construct to in the beginning (e.g. a noexcept default constructor).

Yes, there are ways of doing this in C++98/03, but they aren't as pretty. You have to create an implementation-detail base class of X that contains the destruction logic of X, but not the construction logic. Been there, done that, I love delegating constructors.


I think the problem stems from a violation of the Single Responsibility Principle: Class X has to deal with managing the lifetimes of multiple objects (and that is probably not even its main responsibility).

The destructor of a class should only free the resources that the class has directly acquired. If the class is just a composite (i.e. an instance of the class owns instances of other classes) it should ideally rely on automatic memory management (via RAII) and just use the default destructor. If the class has to manage some specialized resources manually (e.g. opens a file descriptor or a connection, acquires a lock or allocates memory) I would recommend factoring out the responsibility of managing those resources to a class dedicated for this purpose and then using instances of that class as members.

Using the standard template library would in fact help because it contains data structures (such as smart pointers and std::vector<T>) that exclusively handle this problem. They can also be composed, so even if your X has to contain multiple instances of objects with complicated resource acquisition strategies, the problem of resource management in an exception safe manner is solved both for each member as well as for the containing composite class X.