skydoor
skydoor

Reputation: 25898

What to do if a failed destructor can't throw an exception

I noticed that you can't throw an exception in a destructor. So my question is what should I do if destructor fails.

Another question is, under what situation a destructor might fail?

Thanks so much

Upvotes: 5

Views: 1681

Answers (5)

paercebal
paercebal

Reputation: 83394

Destructors must not throw!

RAII is built upon this, so throwing from a destructor will open the possibility of RAII breaking on you.

And this is not limited to C++. You usually don't want a failure on disposal to break the execution of your program, whatever the language or framework.

But I wanna throw!!!!

If you believe your object should throw upon disposal of resource, then you should do it manualy:

class MyObject
{
    public :
       // etc.
       ~MyObject()
       {
          try
          {
             this->dispose() ;
          }
          catch(...) { /* log the problem, or whatever, but DON'T THROW */ }
       }

       void dispose()
       {
          if(this->isAlreadyDisposed == false)
          {
             this->isAlreadyDisposed = true ;

             // dispose the acquired resource          
          }
       }
} ;

This way, by default, your object will work correctly with RAII. But on the occasion where dispose failure should be known to you, then you call manually the dispose method and handle the potential failure.

The exact code of the dispose method depends on what result you want (e.g. Should dispose be multithread-safe or not, should an object that failed on dispose be considered "already disposed" or not, etc.).

Isn't there another way to signal the error?

Of course, there is, but they are grounded on global resources anyway.

For example, you could log the failure in the console, or in a text file.

Another would be to set some global variable, but this is usually the dirtiest trick you can use with C++. Another would be to call some kind of handler, but again, you can't do much in a generic handler who won't know what to do with your error.

In one case, I wrote a constructor who took a reference to a boolean upon construction. Something like:

class MyObject
{
    bool & isOk ;

    public :
       // etc.
       MyObject(bool & p_isOk) : isOk(p_isOk) {}

       ~MyObject()
       {
          // dispose of the ressource
          // If failure, set isOk to false ;
       }
} ;

Which can be used as:

void foo()
{
   bool isOk = true ;

   // etc.

   {
      MyObject o(isOk) ;
      // etc.
   }


   if(isOk == false)
   {
      // Oops...
   }
}

But this kind of code should be exceptional. I remember I imagined it, but can't remember if it was used at all (I used a variation for a timer, though...).

Upvotes: 2

Steve Jessop
Steve Jessop

Reputation: 279385

Ignore the error.

A destructor might "fail" if for example the class wraps some kind of output, and the destructor flushes and closes that output. Writing data might fail. Your options then are to terminate the program, or to catch the exception, ignore the error, and return. Usually the right design is to ignore it.

In my example, the class should also have a "close_and_flush" function, which users can call prior to object destruction if they want to know whether it succeeded or not. If the user of your class doesn't care whether the operation failed, then neither do you, and you can safely suppress the exception.

Users can then write code like this:

{
    OutputObject OO;
    write some stuff to OO, might throw;
    do more things, might throw;
    try {
        OO.flush_and_close();
    } catch (OutputException &e) {
        log what went wrong;
        maybe rethrow;
    }
}

Or this:

try {
    OutputObject OO;
    write some stuff to OO, might throw;
    do more things, might throw;
    OO.flush_and_close();
} catch (AnyOldException &e) {
    log what went wrong;
    maybe rethrow;
}

Either way, the only time the object will be destroyed without explicit flushing by the user, is if something else throws an exception and the object is destroyed during stack unwinding. So they already know that their operation has failed, and if necessary they can roll back transactions or whatever else they have to do in response to failure.

Upvotes: 14

anon
anon

Reputation:

I disagree with the idea that destructors should be "designed so they cannot fail" - of course they can fail. For example, a call to fclose() in a destructor could fail. Now the question is what to do about it? As I see it, there are two options:

  • Ignore it. This has the virtue of simplicity, but you will never know that the failure happened, which can mean hiding bugs.

  • Log it. The problem with this is that there is no guarantee that writing the log won't also fail, or trigger some other problem.

If this leaves you no wiser, then it's the same for me! Basically, there is no perfect solution. You need to choose from the two options above depending on circumstances. One way of deciding is to think "If this were C code (which doesn't have destructors) what would I do?" - if you would ignore the problem in C, ignore it in C++ too,

Upvotes: 6

Holograham
Holograham

Reputation: 1368

A destructor should be written so that it cannot fail. Freeing resources should be the only things you do in the destructor itself. Use separate methods for further "de-initialization" procedures.

Upvotes: 3

Alexander Gessler
Alexander Gessler

Reputation: 46657

Design your classes that their d'tors can't fail by design. If something in the d'tor could throw an exception, catch it within the d'tor body and swallow it (or handle it however you like it, but don't rethrow it).

Upvotes: 8

Related Questions