Rule 3: Two-phase construction

Sometimes a constructor will need to allocate resources, such as memory. The most ubiquitous case is that of a compound -class: If a compound class contains a pointer to another -class, it will need to allocate memory for that class during its own construction.

Note: C-classes in Symbian OS are always allocated on the heap, and always have CBase as their ultimate base class.

In the following example, CMyCompoundClass has a data member that is a pointer to a CMySimpleClass

Here is the definition for CMySimpleClass

class CMySimpleClass : public CBase
    {
 public:
    CMySimpleClass();
    ~CMySimpleClass();
     ...
private:
    Tint iSomeData;
};

Here is the definition for CMyCompoundClass:

class CMyCompoundClass : public CBase
    {
public:
    CMyCompoundClass();
    ~CMyCompoundClass();

private:
    CMySimpleClass* iSimpleClass; // owns another C-class
    };

If you write the constructor for CMyCompoundClass as follows:

CMyCompoundClass::CMyCompoundClass()
    {
    iSimpleClass = new CMySimpleClass; // WRONG
    }

Consider what happens when a new CMyCompoundClass is created:

CMyCompoundClass* myCompoundClass = new( ELeave ) CMyCompoundClass;

With the above constructor, the following sequence of events occurs:

  1. Memory is allocated for the instance of CMyCompoundClass.

  2. The constructor of CMyCompoundClass is called.

  3. The constructor creates a new instance of CMySimpleClass and stores a pointer to it in iSimpleClass.

  4. The constructor completes.

But, if stage 3 fails due to insufficient memory, what happens? There is no way to return an error code from the constructor to indicate that the construction was not completed. The new operator will return a pointer to the memory that was allocated for the CMyCompoundClass, but it will point to a partially constructed object.

If we make the constructor leave, we can detect when the object was not fully constructed, as follows:

CMyCompoundClass::CMyCompoundClass()// WRONG
    {
    iSimpleClass = new( ELeave ) CMySimpleClass;
    }

However, this is not a viable method for indicating that an error has occurred, because we have already allocated memory for the instance of CMyCompoundClass. A leave would destroy the pointer to the allocated memory (this), and there would be no way to free it, resulting in a memory leak.

The solution is to allocate all of the memory for the components of the object, after the C++ constructor has initialized the compound object. In Symbian OS this is performed in a ConstructL() method by convention. For example:

void CMyCompoundClass::ConstructL()// RIGHT
    {
    iSimpleClass = new( ELeave ) CMySimpleClass;
    }

The C++ constructor should contain only initialization code that cannot leave (if any):

CMyCompoundClass::CMyCompoundClass()  // RIGHT
    {
    // Initialization that cannot leave.
    }

The object is now constructed as follows:

CMyCompoundClass* myCompoundClass = new( ELeave ) CMyCompoundClass;
CleanupStack::PushL( myCompoundClass );
myCompoundClass->ConstructL();      // RIGHT

This can be encapsulated in a NewL() or NewLC() method for convenience.

Implementing two-phase construction using NewL() / NewLC()

If a compound object has a NewL() method (or NewLC()), then this should contain both stages of construction. After the allocation stage, the object should be pushed onto the cleanup stack before ConstructL() is called, in case ConstructL() leaves. For example:

CMyCompoundClass* CMyCompoundClass::NewLC() 
    {
    CMyCompoundClass* self = new( ELeave ) CMyCompoundClass;
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

CMyCompoundClass* CMyCompoundClass::NewL()
    {
    CMyCompoundClass* self = new( ELeave ) CMyCompoundClass;
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop(); // self
    return self;
    }