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 succeedsm_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++!