C++: is "delete (base_class*)this;" safe?

Moderator
Posts: 700
Joined: 2002.04
Post: #1
Okay, I've a base class containing a lot of resources and a simple reference counting system for allocating or deallocating those resources as necessary, shared between several derived classes.

I'd failed to consider that if an exception occurs in the constructor of any of the derived classes, the destructor of the base class won't be invoked, and the reference counting system will either at best hold an incorrect (+1) value, or at worst fail to deallocate the resources.

I think that this problem can be solved by catching any exception which occurs in the constructors of the derived classes, invoking "delete (base_class*)this;", and rethrowing the exception.

I'm just wondering whether anybody can confirm whether this is a safe technique? The quick and dirty test below seems to give a safe result, but searching hasn't given me a definitive answer, just the usual dire warnings about mixing C and C++ memory management...

Code:
#include <stdio.h>

class A
{
    private:
        static unsigned int mg_noOfInstances;
        // Lots of static pointers.

    public:
        A( void )
        {
            // Allocate static pointers if mg_noOfInstances == 0, throw exception if mg_noOfInstances == UINT_MAX.
            mg_noOfInstances ++;
            printf( "A()\nmg_noOfInstances == %d\n", mg_noOfInstances );
            return;
        };

        virtual ~A( void )
        {
            mg_noOfInstances --;
            // Deallocate static pointers if mg_noOfInstances == 0.
            printf( "~A()\nmg_noOfInstances == %d\n", mg_noOfInstances );
            return;
        };
};

unsigned int A::mg_noOfInstances = 0;

class B : private A
{
    public:
        B( void )
        {
            printf( "B()\n" );
            // Oh noes! We've encountered an exception; better clean ourselves up before we die...
            delete (A*)this;
            throw "Oh noes!";
        };
};

int main( void )
{
    B *b = new B();
    return 0;
}

Terminal.app Wrote:A()
mg_noOfInstances == 1
B()
~A()
mg_noOfInstances == 0
Abort trap

N.b. I know I could circumvent this problem by making the base class an attribute of the derived classes, and if there's no alternative, I will; I'd just prefer a 'prettier' architecture if at all possible...

Mark Bishop
--
Student and freelance OS X & iOS developer
Quote this message in a reply
Moderator
Posts: 434
Joined: 2002.09
Post: #2
http://www.parashift.com/c++-faq-lite/ex...l#faq-17.3

Short answer: don't throw exceptions in destructors, don't expect to safely be able to handle exceptions in destructors.

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #3
The destructor of your base class *will* be called if the constructor of a subclass throws:

Code:
extern "C" void puts(char const *);

class A {
public:
    virtual ~A() { puts("~A"); }
};

class B : public A {
public:
    B() { throw 1; }
    ~B() { puts("~B"); }
};

main() { try { B b; } catch(int) { puts("gotcha"); } }

Code:
keith$ g++ test.cc
keith$ ./a.out
~A
gotcha

... so I don't know what your problem is.
Quote this message in a reply
Moderator
Posts: 700
Joined: 2002.04
Post: #4
Ugh... Thanks OSC, I finally figured out the difference between your and my code which was causing the difference in behaviour: that the destructor of the base class is only invoked if the exception is caught, which of course is obvious in hindsight Blush

Mark Bishop
--
Student and freelance OS X & iOS developer
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Way to tell what delete is doing? ia3n_g 6 2,939 Aug 26, 2006 12:05 PM
Last Post: akb825