Wednesday 15 August, 2007

Zombie

This article talks about handling a failing constructor.

What does it mean when I say Failing Constructor?

Consider a situation where we have written a constructor for a class and for some reasons it fails before fully executing the constructor. Constructor wont return any value, so it wont be possible for us to tell whether the constructor is fully executed or not.

What's the state of the object in this case?

Its a zombie object, because of the reason that the construction of the object has not happened in the expected way.

What's the problem with zombie objects?

Constructor has not executed completely, it has ended unexpectedly. This means the object has not been constructed. Hence, Destructor for the same object wont be executed. We will encounter problem, If we have done any dynamic allocations or opened any resource handles in the constructor before it fails, because the memory allocated wont be freed since the code for freeing up the memory or closing the resource handles usually sits in the destructor.

Code below explains this situation,

class CTest
{
public:
    // Constructor
    CTest();

    // Destructor
    ~CTest();

private:
    // Member pointer variables
    char *m_ptr1;
    char *m_ptr2;
};

// Constructor definition
CTest::CTest()
{
    // Init member pointers
    m_ptr1 = m_ptr2 = NULL;

    m_ptr1 = new char[100]; // Assume this succeeds

    m_ptr2 = new char[100]; // Assume this fails here!

    // Other processing

    ...
}

// Destructor definition
CTest::~CTest()
{
    delete [] m_ptr1;
    delete [] m_ptr2;
}

Have a look at the constructor CTest::CTest(), assume the first dynamic allocation succeeded for m_ptr1 and m_ptr2 failed for some reasons. Since the object is not constructed fully destructor wont be called and hence the heap allocations are not freed.

How do we overcome this problem?

One solution would be to catch the exception, free the allocated memory and then re throw the exception. Following code will show this technic.

// Constructor definition
CTest::CTest()
{
    // Init member pointers
    m_ptr1 = m_ptr2 = NULL;

    // try block
    try
    {
        m_ptr1 = new char[100]; // Assume this succeeds

        m_ptr2 = new char[100]; // Assume this fails here!

        // Other processing

        ...
    }
    // catch block
    catch(...)
    {
        // free the resources
        delete [] m_ptr1;
        delete [] m_ptr2;

        // re-throw
        throw;
    }
}

Another solution would be to use smart pointers, instead of just the raw pointers. When exception is thrown, the destructors for the local variables will be called as part of stack-unwinding which calls the destructor of the smart pointer which in turn frees the resources.

Enjoy C++!

2 comments:

Anonymous said...

what "rethrow" is doing here? where it is rethrowing - to main()?? if so what should be the catch handler of main()?

Prashanth Narayanaswamy said...

@ Ibrahim,
Re-throw is a must here.

Thats how the caller (who creates this object) knows that the constructor failed.

The caller should have a try block again to catch the failure and take necessary steps to handle the failure.

Constructor must always throw exception whenever it fails.