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.
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.
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:
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.
Push the object to the clean-up stack after it has been allocated.
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()
.