Monday, December 13, 2010

How to handle a constructor and destructor that fail

Constructors don't have a return type, so it's not possible to use return codes. The best way to signal constructor failure is to throw an exception.

In any constructor that initializes member objects, it is always better to use an initialization list. There can be special scenarios that require one to use the initialization list (to initialize a constants or references in the class). There are other advantages to set the objects using an initialization list rather than within the constructor itself. What happens if one of the one of the members fails at initialization and throw an exception?

How will you handle such a situation? It’s not quite hard.

Constructor function-try-block


class Myclass
{

Myclass ()
try
: m_member1(value1),
m_member2(value2)
{
// Rest of constructor code
}
catch (...)
{
// Got here if one of the members in initilization list throws.
}

private:

type m_member1;
type m_member2
};


How ever one of the drawbacks is that you should throw an exception from the catch block. You cannot simple ignore and continue with the object creation!!!

Destructors on the other hand should be exception free. Destructors should be written in a way in which they cannot fail. Resource Acquisition Is Initialization is built upon the lines that resources are acquired during initialization, when there is no chance of them being used before they are available, and released with the destruction of the same objects, throwing from a destructor will open the possibility of RAII failing on you. If they can, it is a sign for a very bad design.

Some of the morals and further discussions from http://www.gotw.ca/gotw/066.htm

Moral #1: Constructor function-try-block handlers have only one purpose -- to translate an exception. (Or for logging) They are not useful for any other purpose.

Moral #2: Since destructors should never emit an exception, destructor function-try-blocks have no practical use at all. It is of not much use as it can not suppress the exception.

Moral #3: Always perform unmanaged resource acquisition in the constructor body, never in initializer lists. In other words, either use "resource acquisition is initialization" (thereby avoiding unmanaged resources entirely) or else perform the resource acquisition in the constructor body.

Moral #4: Always clean up unmanaged resource acquisition in local try-block handlers within the constructor or destructor body, never in constructor or destructor function-try-block handlers.

Moral #5: If a constructor has an exception specification, that exception specification must allow for the union of all possible exceptions that could be thrown by base and member subobjects.

Moral #6: If a constructor of a member object can throw but you can get along without said member, hold it by pointer and use the pointer's nullness to remember whether you've got one or not, as usual. Use the Pimpl idiom to group such "optional" members so you only have to allocate once.

Moral #7: Prefer using "resource acquisition is initialization" to manage resources.

No comments: