diff -r 4816d766a08a -r f345bda72bc4 Symbian3/PDK/Source/GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C.dita --- a/Symbian3/PDK/Source/GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C.dita Tue Mar 30 11:42:04 2010 +0100 +++ b/Symbian3/PDK/Source/GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C.dita Tue Mar 30 11:56:28 2010 +0100 @@ -1,58 +1,107 @@ - - - - - -How to implement two-phase construction

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().

\ No newline at end of file + + + + + +How +to implement two-phase constructionThis 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. +
  3. Call the ConstructL() function +to complete construction.

  4. +

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().

  • +
+
\ No newline at end of file