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);
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:
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
because of the leave, there is no valid pointer for the
partially-constructed CCompound
without a valid pointer, there is no way to clean up the
CCompound
The solution is to allocate the CCompound
, 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 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()
examplevoid CCompound::ConstructL()
{
// NB. function may leave, as CSimple::NewL may leave
iChild = new (ELeave) CSimple (iVal);
}
Implement NewL()
by doing a NewLC()
, followed
by popping the pushed pointer from the cleanup stack:
NewL()
exampleCCompound* CCompound::NewL(TInt aVal)
{
CCompound* self=NewLC(aVal);
CleanupStack::Pop();
return self;
}
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()
.