throwing exceptions out of a destructor

2018-12-31 04:34发布

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.

16条回答
心情的温度
2楼-- · 2018-12-31 04:35

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.

查看更多
浪荡孟婆
3楼-- · 2018-12-31 04:35

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?

查看更多
骚的不知所云
4楼-- · 2018-12-31 04:42

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.

查看更多
流年柔荑漫光年
5楼-- · 2018-12-31 04:42

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

查看更多
弹指情弦暗扣
6楼-- · 2018-12-31 04:43

Unlike constructors, where throwing exceptions can be a useful way to indicate that object creation succeeded, exceptions should not be thrown in destructors.

The problem occurs when an exception is thrown from a destructor during the stack unwinding process. If that happens, the compiler is put in a situation where it doesn’t know whether to continue the stack unwinding process or handle the new exception. The end result is that your program will be terminated immediately.

Consequently, the best course of action is just to abstain from using exceptions in destructors altogether. Write a message to a log file instead.

查看更多
春风洒进眼中
7楼-- · 2018-12-31 04:44

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 ]

查看更多
登录 后发表回答