2

It seems you cannot throw exceptions from destructors in case more than one exception is thrown from the destructor.

In Effective C++ Scott Meyers uses a vector example where the first element throws an exception during destruction and then later a second element throws- causing problems for C++ (which cannot handle more than one exception).

Surely this scenario (vectors manipulating elements and elements throwing exceptions) could also happen during a copy constructors implementing deep copying though?

EDIT:

Are we saying that its the recursive-nature of dtors calling underlying dtors which is different to the copy constructor?

user997112
  • 29,025
  • 43
  • 182
  • 361
  • 2
    You *can* throw from a destructor. It just leads to very nasty behaviour (`abort`) sometimes. It is essential that you *can* throw in a copy constructor; those typically also need to acquire resources, and that may fail. – dyp Jun 21 '14 at 21:02
  • 3
    Throwing an exceptions unwinds the stack. Unwinding the stack runs dtors on the objects on the stack. C++ says if unwind gets an exception then the C++ runtime calls terminate(). By default, terminate() calls abort(). – brian beuning Jun 21 '14 at 21:05
  • 1
    What is your question? Is it "why shouldn't you throw from destructors?" or is it "why is it OK to throw from a copy constructor?"? – D Drmmr Jun 21 '14 at 21:20
  • @DDrmmr yes- why is it ok to throw from a copy construstor? and what is it about the copy constructor which is different from the dtor in this respect? (so its kinda both Qs). – user997112 Jun 22 '14 at 00:17
  • "Surely this scenario (vectors manipulating elements and elements throwing exceptions) could also happen during a copy constructors implementing deep copying though?" - Try to come up with an example where this happens! – Neil Kirk Jun 22 '14 at 00:26
  • When a copy constructor fails then an object is not copied. One can live with that and continue. If a destructor fails you have an indestructible object. Now imagine the object is on the stack and a function tries to return. The stack cannot be cleaned up and the function cannot return. There is nothing useful you can do in that situation besides killing the program. Also the concept of indestructible objects is not very useful. – nwp Jun 22 '14 at 00:34
  • @nwp I can think of a few uses of indestructible objects! – Neil Kirk Jun 22 '14 at 00:43

2 Answers2

4

Exceptions shouldn't be thrown from destructors because when you throw an exception, the compiler will clean the variables in scope, thus calling their destructors. You'd be in a bad shape if those threw exceptions too.

Ven
  • 19,015
  • 2
  • 41
  • 61
  • Did you mean the "compiler" does the cleaning? Its at run-time surely? So are you saying the problem with destructors is the recursive nature of destructors possibly calling other destructors? I'm not sure I understand why its find to throw exceptions from copy constructors? – user997112 Jun 21 '14 at 21:06
  • The compiler does the cleanup like when you leave any scope : All variables in scope are removed (and their dtors are called). You don't call copy constructors when you free variables on the stack. – Ven Jun 21 '14 at 21:07
  • 2
    The compiler generates code that does the cleanup and/or just assume the used variable space as free again (so it can be used for other code part variables again)... – deviantfan Jun 21 '14 at 21:14
  • @user997112 The problem is only one exception can be active at a time. If an exception is thrown, and the stack is unwound, causing an object's destructor to throw an exception, what about the first exception? There are now two active at the same time, which isn't allowed. (This is a simplification - exceptions can be thrown inside the destructor so long as they are caught). – Neil Kirk Jun 22 '14 at 00:25
  • somewhat outdated advice given that std::uncaught_exceptions was added in C++17, which can be used to detect if the stack is unwinding because of a different exception being throw, thus preventing double throw which will take the program down; whether it's the right semantics to throw sometimes and not other times, that up to your design, but the capability is there –  Nov 26 '17 at 10:41
2

What is different between constructors and destructors (with regard to throwing) is that

  • Firstly, destruction of objects is tied to unwinding, and unwinding happens when exceptions are thrown. Unwinding is not tied to construction of objects. That is to say, there is a relationship between destructors and exceptions already.

  • When some code is interrupted during the construction of an object, this can be safely handled. Resources can be rolled back as if the creation of the object had never been requested. Furthermore, it doesn't matter that subsequent code doesn't run, like the construction of other objects. In the case of destruction, it is essential that it be done properly, otherwise resources leak. It is not okay to abandon a destructor, because that is the last chance for cleaning up an object. Moreover, if the destructor is part of a chain of multiple calls, it is not okay for the remaining destructors to never be called. Neglect to execute destructors will lead to some permanent problem in the program.

Suppose you have a situation like this:

{
  A a;
  B b;
  C c;
  // ...
}

Suppose that the statments in the block throw, so the destructors for A, B and C are carried out in reverse order: C::~C then ~B::B and A::~A. Suppose that C::~C throws. This means that not only will the destruction of c be incomplete, but that the destruction of B and A will not happen. The clean-up code for this statement block is simply abandoned.

What should the semantics be?

If none of the destructors can be abandoned in this situation, that means that C::~C has to re-execute if it throws. But that will likely trigger an infinite loop.

Or, unwinding could still do the remaining destructors for b and a. But that still means that the C destructor was abandoned and C is not properly destroyed.

Kaz
  • 55,781
  • 9
  • 100
  • 149