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

Viewed 138166

Most people say never throw an exception out of a destructor - doing so results in undefined behavior. Stroustrup makes the point that "the vector destructor explicitly invokes the destructor for every element. This implies that if an element destructor throws, the vector destruction fails... There is really no good way to protect against exceptions thrown from destructors, so the library makes no guarantees if an element destructor throws" (from Appendix E3.2).

This article seems to say otherwise - that throwing destructors are more or less okay.

So my question is this - if throwing from a destructor results in undefined behavior, how do you handle errors that occur during a destructor?

If an error occurs during a cleanup operation, do you just ignore it? If it is an error that can potentially be handled up the stack but not right in the destructor, doesn't it make sense to throw an exception out of the destructor?

Obviously these kinds of errors are rare, but possible.

17 Answers

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

Throwing out of a destructor can result in a crash, because this destructor might be called as part of "Stack unwinding". Stack unwinding is a procedure which takes place when an exception is thrown. In this procedure, all the objects that were pushed into the stack since the "try" and until the exception was thrown, will be terminated -> their destructors will be called. And during this procedure, another exception throw is not allowed, because it's not possible to handle two exceptions at a time, thus, this will provoke a call to abort(), the program will crash and the control will return to the OS.

The real question to ask yourself about throwing from a destructor is "What can the caller do with this?" Is there actually anything useful you can do with the exception, that would offset the dangers created by throwing from a destructor?

If I destroy a Foo object, and the Foo destructor tosses out an exception, what I can reasonably do with it? I can log it, or I can ignore it. That's all. I can't "fix" it, because the Foo object is already gone. Best case, I log the exception and continue as if nothing happened (or terminate the program). Is that really worth potentially causing undefined behavior by throwing from a destructor?

Its dangerous, but it also doesn't make sense from a readability/code understandability standpoint.

What you have to ask is in this situation

int foo()
{
   Object o;
   // As foo exits, o's destructor is called
}

What should catch the exception? Should the caller of foo? Or should foo handle it? Why should the caller of foo care about some object internal to foo? There might be a way the language defines this to make sense, but its going to be unreadable and difficult to understand.

More importantly, where does the memory for Object go? Where does the memory the object owned go? Is it still allocated (ostensibly because the destructor failed)? Consider also the object was in stack space, so its obviously gone regardless.

Then consider this case

class Object
{ 
   Object2 obj2;
   Object3* obj3;
   virtual ~Object()
   {
       // What should happen when this fails? How would I actually destroy this?
       delete obj3;

       // obj 2 fails to destruct when it goes out of scope, now what!?!?
       // should the exception propogate? 
   } 
};

When the delete of obj3 fails, how do I actually delete in a way that is guaranteed to not fail? Its my memory dammit!

Now consider in the first code snippet Object goes away automatically because its on the stack while Object3 is on the heap. Since the pointer to Object3 is gone, you're kind of SOL. You have a memory leak.

Now one safe way to do things is the following

class Socket
{
    virtual ~Socket()
    {
      try 
      {
           Close();
      }
      catch (...) 
      {
          // Why did close fail? make sure it *really* does close here
      }
    } 

};

Also see this FAQ

From the ISO draft for C++ (ISO/IEC JTC 1/SC 22 N 4411)

So destructors should generally catch exceptions and not let them propagate out of the destructor.

3 The process of calling destructors for automatic objects constructed on the path from a try block to a throw- expression is called “stack unwinding.” [ Note: If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. — end note ]

Your destructor might be executing inside a chain of other destructors. Throwing an exception that is not caught by your immediate caller can leave multiple objects in an inconsistent state, thus causing even more problems then ignoring the error in the cleanup operation.

Everyone else has explained why throwing destructors are terrible... what can you do about it? If you're doing an operation that may fail, create a separate public method that performs cleanup and can throw arbitrary exceptions. In most cases, users will ignore that. If users want to monitor the success/failure of the cleanup, they can simply call the explicit cleanup routine.

For example:

class TempFile {
public:
    TempFile(); // throws if the file couldn't be created
    ~TempFile() throw(); // does nothing if close() was already called; never throws
    void close(); // throws if the file couldn't be deleted (e.g. file is open by another process)
    // the rest of the class omitted...
};

As an addition to the main answers, which are good, comprehensive and accurate, I would like to comment about the article you reference - the one that says "throwing exceptions in destructors is not so bad".

The article takes the line "what are the alternatives to throwing exceptions", and lists some problems with each of the alternatives. Having done so it concludes that because we can't find a problem-free alternative we should keep throwing exceptions.

The trouble is is that none of the problems it lists with the alternatives are anywhere near as bad as the exception behaviour, which, let's remember, is "undefined behaviour of your program". Some of the author's objections include "aesthetically ugly" and "encourage bad style". Now which would you rather have? A program with bad style, or one which exhibited undefined behaviour?

Q: So my question is this - if throwing from a destructor results in undefined behavior, how do you handle errors that occur during a destructor?

A: There are several options:

  1. Let the exceptions flow out of your destructor, regardless of what's going on elsewhere. And in doing so be aware (or even fearful) that std::terminate may follow.

  2. Never let exception flow out of your destructor. May be write to a log, some big red bad text if you can.

  3. my fave : If std::uncaught_exception returns false, let you exceptions flow out. If it returns true, then fall back to the logging approach.

But is it good to throw in d'tors?

I agree with most of the above that throwing is best avoided in destructor, where it can be. But sometimes you're best off accepting it can happen, and handle it well. I'd choose 3 above.

There are a few odd cases where its actually a great idea to throw from a destructor. Like the "must check" error code. This is a value type which is returned from a function. If the caller reads/checks the contained error code, the returned value destructs silently. But, if the returned error code has not been read by the time the return values goes out of scope, it will throw some exception, from its destructor.

Throwing an exception out of a destructor never causes undefined behaviour.

The problem of throwing exceptions out a destructor is that destructors of successfully created objects which scopes are leaving while handling an uncaught exception (it is after an exception object is created and until completion of a handler of the exception activation), are called by exception handling mechanism; and, If such additional exception from the destructor called while processing the uncaught exception interrupts handling the uncaught exception, it will cause calling std::terminate (the other case when std::exception is called is that an exception is not handled by any handler but this is as for any other function, regardless of whether or not it was a destructor).


If handling an uncaught exception in progress, your code never knows whether the additional exception will be caught or will archive an uncaught exception handling mechanism, so never know definitely whether it is safe to throw or not.

Though, it is possible to know that handling an uncaught exception is in progress ( https://en.cppreference.com/w/cpp/error/uncaught_exception), so you are able to overkill by checking the condition and throw only if it is not the case (it will not throw in some cases when it would be safe).

But in practice such separating into two possible behaviours is not useful - it just does not help you to make a well-designed program.


If you throw out of destructors ignoring whether or not an uncaught exception handling is in progress, in order to avoid possible calling std::terminate, you must guarantee that all exceptions thrown during lifetime of an object that may throw an exception from their destructor are caught before beginning of destruction of the object. It is quite limited usage; you hardly can use all classes which would be reasonably allowed to throw out of their destructor in this way; and a combination of allowing such exceptions only for some classes with such restricted usage of these classes impede making a well-designed program, too.

I currently follow the policy (that so many are saying) that classes shouldn't actively throw exceptions from their destructors but should instead provide a public "close" method to perform the operation that could fail...

...but I do believe destructors for container-type classes, like a vector, should not mask exceptions thrown from classes they contain. In this case, I actually use a "free/close" method that calls itself recursively. Yes, I said recursively. There's a method to this madness. Exception propagation relies on there being a stack: If a single exception occurs, then both the remaining destructors will still run and the pending exception will propagate once the routine returns, which is great. If multiple exceptions occur, then (depending on the compiler) either that first exception will propagate or the program will terminate, which is okay. If so many exceptions occur that the recursion overflows the stack then something is seriously wrong, and someone's going to find out about it, which is also okay. Personally, I err on the side of errors blowing up rather than being hidden, secret, and insidious.

The point is that the container remains neutral, and it's up to the contained classes to decide whether they behave or misbehave with regard to throwing exceptions from their destructors.

Related