Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is throwing an exception from destructor safe for the vtable?

Please consider the following example:

#include <csignal>

class A
{
public:
    virtual ~A() {}
    virtual void foo() = 0;
};

class B : public A
{
public:
    virtual ~B() { throw 5; } 
    virtual void foo() {}
};

int main(int, char * [])
{
    A * b = new B();

    try
    {
        delete b;
    }
    catch ( ... )
    {
        raise(SIGTRAP);
    }
    return 0;
}

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. But when I tried to run this snippet in gdb and got to the breakpoint in catch section I saw that B object was gone and only A base object left because the vtable looked like this:

(gdb) i vtbl b
vtable for 'A' @ 0x400cf0 (subobject @ 0x603010):
[0]: 0x0
[1]: 0x0
[2]: 0x4008e0 <__cxa_pure_virtual@plt>

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

like image 710
krokoziabla Avatar asked Nov 30 '15 20:11

krokoziabla


People also ask

What happens when an exception is thrown in a destructor?

Throwing an exception out of a destructor is dangerous. If another exception is already propagating the application will terminate.

Can I throw an exception from a constructor from a destructor?

When an exception is thrown from a constructor, the object is not considered instantiated, and therefore its destructor will not be called. But all destructors of already successfully constructed base and member objects of the same master object will be called.

Is destructor called when exception is thrown?

When an exception is thrown, destructors of the objects (whose scope ends with the try block) are automatically called before the catch block gets executed. That is why the above program prints “Destructing an object of Test” before “Caught 10“.

Which of the following should not throw an exception?

I also know that the following cannot throw exceptions either: Destructors. Reading/writing primitive types.


2 Answers

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.

like image 181
Baum mit Augen Avatar answered Oct 02 '22 22:10

Baum mit Augen


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.

No. The lifetime of an object ends when its destructor starts.

You cannot cancel a destructor.

As others said, throwing destructors in C++ are weird and you want to avoid them except for special cases.

like image 33
Martin Ba Avatar answered Oct 02 '22 22:10

Martin Ba