If You Shouldn't Throw Exceptions in a Destructor, How to Handle Errors in It

If you shouldn't throw exceptions in a destructor, how do you handle errors in it?

Throwing an exception out of a destructor is dangerous.

If another exception is already propagating the application will terminate.

#include <iostream>

class Bad
{
public:
// Added the noexcept(false) so the code keeps its original meaning.
// Post C++11 destructors are by default `noexcept(true)` and
// this will (by default) call terminate if an exception is
// escapes the destructor.
//
// But this example is designed to show that terminate is called
// if two exceptions are propagating at the same time.
~Bad() noexcept(false)
{
throw 1;
}
};
class Bad2
{
public:
~Bad2()
{
throw 1;
}
};


int main(int argc, char* argv[])
{
try
{
Bad bad;
}
catch(...)
{
std::cout << "Print This\n";
}

try
{
if (argc > 3)
{
Bad bad; // This destructor will throw an exception that escapes (see above)
throw 2; // But having two exceptions propagating at the
// same time causes terminate to be called.
}
else
{
Bad2 bad; // The exception in this destructor will
// cause terminate to be called.
}
}
catch(...)
{
std::cout << "Never print this\n";
}

}

This basically boils down to:

Anything dangerous (i.e. that could throw an exception) should be done via public methods (not necessarily directly). The user of your class can then potentially handle these situations by using the public methods and catching any potential exceptions.

The destructor will then finish off the object by calling these methods (if the user did not do so explicitly), but any exceptions throw are caught and dropped (after attempting to fix the problem).

So in effect you pass the responsibility onto the user. If the user is in a position to correct exceptions they will manually call the appropriate functions and processes any errors. If the user of the object is not worried (as the object will be destroyed) then the destructor is left to take care of business.

An example:

std::fstream

The close() method can potentially throw an exception.
The destructor calls close() if the file has been opened but makes sure that any exceptions do not propagate out of the destructor.

So if the user of a file object wants to do special handling for problems associated to closing the file they will manually call close() and handle any exceptions. If on the other hand they do not care then the destructor will be left to handle the situation.

Scott Myers has an excellent article about the subject in his book "Effective C++"

Edit:

Apparently also in "More Effective C++"

Item 11: Prevent exceptions from leaving destructors

c++ exception in destructor

The recommendation that "we should not throw exception in destructor" is not an absolute. The issue is that when an exception is thrown the compiler starts unwinding the stack until it finds a handler for that exception. Unwinding the stack means calling destructors for objects that are going away because their stack frame is going away. And the thing that this recommendation is about occurs if one of those destructors throws an exception that isn't handled within the destructor itself. If that happens, the program calls std::terminate(), and some folks think that the risk of that happening is so severe that they have to write coding guidelines to prevent it.

In your code this isn't a problem. The destructor for B throws an exception; as a result, the destructor for a is also called. That destructor throws an exception, but handles the exception inside the destructor. So there's no problem.

If you change your code to remove the try ... catch block in the destructor of A, then the exception thrown in the destructor isn't handled within the destructor, so you end up with a call to std::terminate().

EDIT: as Brian points out in his answer, this rule changed in C++11: destructors are implicitly noexcept, so your code should call terminate when the the B object is destroyed. Marking the destructor as noexcept(false) "fixes" this.

When is it OK to throw an exception from a destructor in C++?

Just don't do it.
If the stars and planets align in such a way that you find you need to...

Still don't do it.

Why I couldn't handle an exception thrown from a destructor outside of it?

I think you misunderstood the recommendation:

a destructor usually should not throw an exception like STL containers and if it should, that thrown expression should be wrapped in a try-catch block (a catch must handle that exception).

In other words, it says:

You should not throw an exception from a destructor.

Now the question arises what one should do if the destructor is:

~Bar() {  
do_something();
}

and do_something() might throw an exception. In this case the recommendation is to catch the exception and handle it:

~Bar() {
try {
do_something();
} catch(...) {
// handle exception, eg write a log message
// but do not retrow it or a different one!
}
}

Now there is no way the exception leaves the destructor.


As you discovered, catching the exception outside of the destructor does not help to solve the general problem, because here:

 try{
Foo f{};
Foo* pFoo = new Foo{};
delete pFoo; // normally exception caught here nad handled by the following catch
}

The call to delete pFoo; throws an exception. During stack unwinding Foo f{}; will be destroyed and another exception will be thrown from the destructor. If an exception is thrown during stack-unwinding std::terminate will be called. And the way to avoid that to happen is to not throw exceptions from destructors.

Handling exceptions inside destructor (but not throwing out)

The question is, is this normal approach to handle exceptions inside destrucotrs? are there any examples that could make this design go wrong?

Yes, you can avoid throwing destructors like this if your // handle exception here code actually handles the exception. But in practice, if you are throwing an exception during destruction it usually implies that there is no good way to handle the exception.

Throwing from a destructor means that some sort of cleanup failed. Maybe a resource is leaked, data couldn't be saved and is now lost or some internal state couldn't be set or reverted. Whatever the cause if you could avoid or fix the problem you wouldn't have to throw in the first place.

Your solution to this bad situation (throwing destructor) only works when you aren't actually in the bad situation. In practice, if you try to apply this you will find that there isn't anything to write // handle exception here, except maybe warning the user or logging the problem.



if only one exception is allowed during stack unwinding?

There is no such rule. The problem with throwing during stack unwinding is if an uncaught exception escapes from a destructor. If the destructor throws and catches exceptions internally, it has no effect on ongoing stack unwindings. std::terminate explicitly states when stack unwinding ends in termination (link) :

In some situations exception handling must be abandoned for less subtle error handling techniques. These situations are:

[...]

-- when the destruction of an object during stack unwinding terminates by throwing an exception, or

[...]

Is throwing an exception from destructor safe for the vtable?

I've always thought (naive me) that when the program gets in this case, into catch section, then object B at which b points will be intact because it's quite logical that the exception will have "cancelled" (if programmed safely) the effect of destructor.

This is not true. The standard says:

An object of any storage duration whose initialization or destruction is terminated by an exception will
have destructors executed for all of its fully constructed subobjects (excluding the variant members of a
union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution
and the destructor has not yet begun execution.

(15.2/2 in N4140)

and, probably more importantly:

The lifetime of an object of type T ends when:

— if T is a class type with a non-trivial destructor (12.4), the destructor call starts

(3.8/1.3 in N4140)

As every member and base of b is completely constructed and non of their destructors where entered yet they will be considered destroyed. So in your catch block, the entire object b points to is already dead.

The rational behind this is probably forbidding "half destructed" objects, as it is unclear what the state of an object that cannot be destroyed should be. For instance, what if only some of the members where destroyed already?

Even the standard itself advises against exceptions leaving destructors. As I wrote in a comment earlier, throwing destructors are weird.

One good rule we can take from the quote above: An object starts to exist when its constructor is done without throwing, and it ceases to exist as soon as its destructor begins execution, regardless of how it exits. (This is restated more clearly at other places in the standard. There are exceptions to this, don't care about them.)

So in conclusion:

is there a way to avoid (half-)destruction of the vtable if I passionately want to throw an exception from a destructor?

No. As soon as you enter the destructor, your object is done for.

Why cant you throw from a destructor. Example

Whenever you throw an exception while exception processing is ongoing, you get a special exception that can't be caught and this leads to an abort.

You can use std::uncaught_exception to detect if exception handling is already in progress and avoid throwing in that case.

C++ throwing an exception from a destructor

Your Component destructor is safe. The rule you're quoting only applies if the exception is thrown out of the destructor (i.e., to the destructor's caller.)

EDIT: Here's one relevant quote from the standard (emphasis added)

Note: If a destructor called during
stack unwinding exits with an
exception, std::terminate is called
(15.5.1).



Related Topics



Leave a reply



Submit