Writing long constructors that perform a number of operations outside of merely initializing data members is generally discouraged due to the inherant problems with error handling. However, there are times when an object must undergo complex initialization before it can be used.
Generally, I prefer to make such initialization as transparent as possible to simplify usage and eliminate the potential for errors from an improperly initialized object. I prefer the two-phase construction idiom over any other approach due to its usage simplicity and safety. I’ll explain this in detail and provide an example.
The idiom requires that you provide a class public static construction method that takes the appropriate arguments and returns an instance to the class. I prefer to call it Create. Like a constructor, the static construction method can be overloaded if necessary. However, unlike a constructor, it can safely throw exceptions if needed and provide return values.
The idiom also requires that your actual constructor(s) is(are) either protected or private. This ensures that an instance can be created only through your static construction method.
Internally, the idiom’s implementation requires that all constructors do only simple data member initialization that is guaranteed to succeed. Initialization of complex data members and other initialization logic that has the potential to fail is contained in a protected or private initializer method. I prefer to call it construct or _construct depending upon its visibility.
The static construction method creates the appropriate instance of the object. It then calls the internal initializer method to carry out the complex and/or failure-potential initialization code. Based upon the success or failure of the initializer, the construction method will either return the new instance or clean up and return an error or throw an exception.
An example of two-phase construction:
class SomeComplexClass
{
public:
static SomeComplexClass * Create();
....
private:
SomeComplexClass();
void _construct();
....
};
....
SomeComplexClass * SomeComplexClass::Create()
{
SomeComplexClass * pNewObj = NULL;
try
{
// create a new instance first
pNewObj = new SomeComplexClass();
// now call the internal initializer and finish building the object
pNewObj->_construct();
}
catch (SomeException & rEx)
{
// clean up the object
delete pNewObj;
pNewObj = NULL;
// pass the exception on
throw;
}
return pNewObj;
}
....
void SomeComplexClass::_construct()
{
....
// perform some complex initialization here that can fail
....
}
....
Usage of this idiom is straight-forward:
....
try
{
SomeComplexClass * pObj = SomeComplexClass::Create();
....
// use the object here
....
// clean up
delete pObj;
pObj = NULL;
}
catch (SomeException & rEx)
{
// TODO: Do some error handling here.
}
....