How to implement two-phase construction

This doccument illustrates two-phase construction with example code.

The example code shown below illustrates the provision of cleanup stack support for CBase -derived classes, and specifically details the motivation behind using a two-phase construction strategy for creating compound objects. It presents two implementations of a CCompound class, one which uses the usual C++ construction strategy, and a second which uses two-phase construction.

Example classes

This section will use the following classes as examples.

CSimple is a simple class whose members do not refer to external resources:

class CSimple : public CBase
    {
public: 
    CSimple(TInt); 
    void Display();
private:
    TInt iVal;
    };

CCompound owns other objects:

class CCompound : public CBase
    {
public:
    void Display();
    ~CCompound();
    static CCompound* NewL(TInt aVal);
    static CCompound* NewLC(TInt aVal); 
protected:
    CCompound(TInt aVal);
    void ConstructL();
private:
    TInt iVal;
    CSimple* iChild;
    };

Note that the constructor is protected, so that CCompound objects can only be created through the public static NewL() and NewLC() functions.

Incorrect construction allowing a memory leak

First consider what would happen if the CSimple object owned by the CCompound were allocated and constructed by the CCompound's constructor, for example:

CCompound::CCompound(TInt aVal)
    {
    iVal=aVal;
    iChild = new (ELeave) CSimple(aVal);  
    }

The problem with this approach is that, if the new in the CCompound's constructor leaves, then:

  • memory has already been allocated for the CCompound object

  • because of the leave, there is no valid pointer for the partially-constructed CCompound object

  • without a valid pointer, there is no way to clean up the CCompound object

Two-phase construction

The solution is to allocate the CCompound object first, push a pointer to the clean-up stack, and then complete its construction. Any construction which might leave must be performed after the partially-constructed CCompound object's address has been pushed to the clean-up stack.

  1. Push the object to the clean-up stack after it has been allocated.

  2. Call the ConstructL() function to complete construction.

NewLC() example

// NewLC with two stage construction
CCompound* CCompound::NewLC(TInt aVal) 
    { 
    // get new, leave if can't
    CCompound* self=new (ELeave) CCompound(aVal);
    
    // push onto cleanup stack in case self->ConstructL leaves
    CleanupStack::PushL(self);
 
    // complete construction with second phase constructor
    self->ConstructL();
    return self;
    }

Now the ConstructL() function is defined instead of the C++ constructor. It performs essentially the same functions as the C++ constructor in the single-phase case:

ConstructL() example

void CCompound::ConstructL() 
    { 
    // NB. function may leave, as creating a new CSimple object
    // may leave.
    iChild = new (ELeave) CSimple (iVal);  
    }

NewL() example

Implement NewL() by doing a NewLC(), followed by popping the pushed pointer from the cleanup stack:

CCompound* CCompound::NewL(TInt aVal) 
    {
    CCompound* self=NewLC(aVal);
    CleanupStack::Pop();
    return self;
    }

Note

  • Two-stage construction for a class could be avoided by including a CleanupStack::PushL(this) at the start of the class's C++ constructor. This would achieve the same effect as using ConstructL(). However if the class is to be used as a base class, the constructor of any classes derived from it will incur the overhead of one push and pop in the constructor called at each level in the inheritance hierarchy, rather than one pop and push in its own NewLC().