Symbian3/PDK/Source/GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C.dita
changeset 5 f345bda72bc4
parent 3 46218c8b8afa
child 14 578be2adaf3e
equal deleted inserted replaced
4:4816d766a08a 5:f345bda72bc4
     7     Nokia Corporation - initial contribution.
     7     Nokia Corporation - initial contribution.
     8 Contributors: 
     8 Contributors: 
     9 -->
     9 -->
    10 <!DOCTYPE concept
    10 <!DOCTYPE concept
    11   PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
    11   PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
    12 <concept xml:lang="en" id="GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C"><title>How to implement two-phase construction</title><prolog><metadata><keywords/></metadata></prolog><conbody><p>The example code shown below illustrates the provision of cleanup stack support for <codeph>CBase</codeph> -derived classes, and specifically details the motivation behind using a two-phase construction strategy for creating compound objects. It presents two implementations of a <codeph>CCompound</codeph> class, one which uses the usual C++ construction strategy, and a second which uses two-phase construction. </p> <section><title>Example classes</title> <p>This section will use the following classes as examples.</p> <p><codeph>CSimple</codeph> is a simple class whose members do not refer to external resources:</p> <codeblock id="GUID-ED0CC311-9A9D-5325-81F9-D9D75EF6A69A" xml:space="preserve">class CSimple : public CBase
    12 <concept id="GUID-60BF8840-CB87-5A39-A46E-76F8D4EDB50C" xml:lang="en"><title>How
       
    13 to implement two-phase construction</title><shortdesc>This doccument illustrates two-phase construction with example
       
    14 code.</shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody>
       
    15 <p>The example code shown below illustrates the provision of cleanup stack
       
    16 support for <codeph>CBase</codeph> -derived classes, and specifically details
       
    17 the motivation behind using a two-phase construction strategy for creating
       
    18 compound objects. It presents two implementations of a <codeph>CCompound</codeph> class,
       
    19 one which uses the usual C++ construction strategy, and a second which uses
       
    20 two-phase construction. </p>
       
    21 <section id="GUID-4219D886-CBB9-4AEC-9B45-7F31BAEE4689"><title>Example classes</title> <p>This section will use the following
       
    22 classes as examples.</p> <p><codeph>CSimple</codeph> is a simple class whose
       
    23 members do not refer to external resources:</p> <codeblock id="GUID-ED0CC311-9A9D-5325-81F9-D9D75EF6A69A" xml:space="preserve">class CSimple : public CBase
    13     {
    24     {
    14 public: 
    25 public: 
    15     CSimple(TInt); 
    26     CSimple(TInt); 
    16     void Display();
    27     void Display();
    17 private:
    28 private:
    27     CCompound(TInt aVal);
    38     CCompound(TInt aVal);
    28     void ConstructL();
    39     void ConstructL();
    29 private:
    40 private:
    30     TInt iVal;
    41     TInt iVal;
    31     CSimple* iChild;
    42     CSimple* iChild;
    32     };</codeblock> <p>Note that the constructor is <codeph>protected</codeph>, so that <codeph>CCompound</codeph> objects can only be created through the public static <codeph>NewL()</codeph> and <codeph>NewLC()</codeph> functions.</p> </section> <section><title>Incorrect construction allowing a memory leak</title> <p>First consider what would happen if the <codeph>CSimple</codeph> object owned by the <codeph>CCompound</codeph> were allocated and constructed by the <codeph>CCompound</codeph>'s constructor, for example:</p> <codeblock id="GUID-1A336A31-5C82-5712-8038-D14F70AB03E7" xml:space="preserve">CCompound::CCompound(TInt aVal)
    43     };</codeblock> <p>Note that the constructor is <codeph>protected</codeph>,
       
    44 so that <codeph>CCompound</codeph> objects can only be created through the
       
    45 public static <codeph>NewL()</codeph> and <codeph>NewLC()</codeph> functions.</p> </section>
       
    46 <section id="GUID-057A405E-3A7B-4C01-887E-38E97A3860C2"><title>Incorrect construction allowing a memory leak</title> <p>First
       
    47 consider what would happen if the <codeph>CSimple</codeph> object owned by
       
    48 the <codeph>CCompound</codeph> were allocated and constructed by the <codeph>CCompound</codeph>'s
       
    49 constructor, for example:</p> <codeblock id="GUID-1A336A31-5C82-5712-8038-D14F70AB03E7" xml:space="preserve">CCompound::CCompound(TInt aVal)
    33     {
    50     {
    34     iVal=aVal;
    51     iVal=aVal;
    35     iChild = new (ELeave) CSimple(aVal);  
    52     iChild = new (ELeave) CSimple(aVal);  
    36     }</codeblock> <p>The problem with this approach is that, if the <codeph>new</codeph> in the <codeph>CCompound</codeph>'s constructor leaves, then:</p> <ul><li id="GUID-FC0C62D4-207D-5A1E-9FE6-967358FF4832"><p>memory has already been allocated for the <codeph>CCompound</codeph> object</p> </li> <li id="GUID-F51CD3ED-2968-500F-89B5-ADD103CF8981"><p>because of the leave, there is no valid pointer for the partially-constructed <codeph>CCompound</codeph> object</p> </li> <li id="GUID-7464BB53-FA84-5D6D-BF43-EB527EDA98DB"><p>without a valid pointer, there is no way to clean up the <codeph>CCompound</codeph> object</p> </li> </ul> </section> <section><title>Two-phase construction</title> <p>The solution is to allocate the <codeph>CCompound</codeph> 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 <codeph>CCompound</codeph> object's address has been pushed to the clean-up stack.</p> <ol id="GUID-02F5717B-1797-5C0F-B4DF-131DA5509226"><li id="GUID-C83A43D7-48D7-553D-9EF2-B2E7783D0D74"><p>Push the object to the clean-up stack after it has been allocated.</p> </li> <li id="GUID-5D99B96E-405D-533A-98FE-885AA60C0862"><p>Call the <codeph>ConstructL()</codeph> function to complete construction. </p> </li> </ol> <p><b>NewLC() example</b> </p> <codeblock id="GUID-46969146-4CE4-52F9-BF2D-9569CDAFF737" xml:space="preserve">// NewLC with two stage construction
    53     }</codeblock> <p>The problem with this approach is that, if the <codeph>new</codeph> in
       
    54 the <codeph>CCompound</codeph>'s constructor leaves, then:</p> <ul>
       
    55 <li id="GUID-FC0C62D4-207D-5A1E-9FE6-967358FF4832"><p>memory has already been
       
    56 allocated for the <codeph>CCompound</codeph> object</p> </li>
       
    57 <li id="GUID-F51CD3ED-2968-500F-89B5-ADD103CF8981"><p>because of the leave,
       
    58 there is no valid pointer for the partially-constructed <codeph>CCompound</codeph> object</p> </li>
       
    59 <li id="GUID-7464BB53-FA84-5D6D-BF43-EB527EDA98DB"><p>without a valid pointer,
       
    60 there is no way to clean up the <codeph>CCompound</codeph> object</p> </li>
       
    61 </ul> </section>
       
    62 <section id="GUID-261B247A-A683-4F29-93CE-DE1EFC3EBD71"><title>Two-phase construction</title> <p>The solution is to allocate
       
    63 the <codeph>CCompound</codeph> object first, push a pointer to the clean-up
       
    64 stack, and then complete its construction. Any construction which might leave
       
    65 must be performed after the partially-constructed <codeph>CCompound</codeph> object's
       
    66 address has been pushed to the clean-up stack.</p> <ol id="GUID-02F5717B-1797-5C0F-B4DF-131DA5509226">
       
    67 <li id="GUID-C83A43D7-48D7-553D-9EF2-B2E7783D0D74"><p>Push the object to the
       
    68 clean-up stack after it has been allocated.</p> </li>
       
    69 <li id="GUID-5D99B96E-405D-533A-98FE-885AA60C0862"><p>Call the <codeph>ConstructL()</codeph> function
       
    70 to complete construction. </p> </li>
       
    71 </ol> <p><b>NewLC()
       
    72 example</b> </p> <codeblock id="GUID-46969146-4CE4-52F9-BF2D-9569CDAFF737" xml:space="preserve">// NewLC with two stage construction
    37 CCompound* CCompound::NewLC(TInt aVal) 
    73 CCompound* CCompound::NewLC(TInt aVal) 
    38     { 
    74     { 
    39     // get new, leave if can't
    75     // get new, leave if can't
    40     CCompound* self=new (ELeave) CCompound(aVal);
    76     CCompound* self=new (ELeave) CCompound(aVal);
    41     
    77     
    43     CleanupStack::PushL(self);
    79     CleanupStack::PushL(self);
    44  
    80  
    45     // complete construction with second phase constructor
    81     // complete construction with second phase constructor
    46     self-&gt;ConstructL();
    82     self-&gt;ConstructL();
    47     return self;
    83     return self;
    48     }</codeblock> <p>Now the <codeph>ConstructL()</codeph> function is defined instead of the C++ constructor. It performs essentially the same functions as the C++ constructor in the single-phase case:</p> <p><b>ConstructL() example</b> </p> <codeblock id="GUID-CA730E71-CE47-54E0-A4CF-A3CAC2FC560B" xml:space="preserve">void CCompound::ConstructL() 
    84     }</codeblock> <p>Now the <codeph>ConstructL()</codeph> function is defined
       
    85 instead of the C++ constructor. It performs essentially the same functions
       
    86 as the C++ constructor in the single-phase case:</p> <p><b>ConstructL() example</b> </p> <codeblock id="GUID-CA730E71-CE47-54E0-A4CF-A3CAC2FC560B" xml:space="preserve">void CCompound::ConstructL() 
    49     { 
    87     { 
    50     // NB. function may leave, as creating a new CSimple object
    88     // NB. function may leave, as creating a new CSimple object
    51     // may leave.
    89     // may leave.
    52     iChild = new (ELeave) CSimple (iVal);  
    90     iChild = new (ELeave) CSimple (iVal);  
    53     }</codeblock> <p><b>NewL() example</b> </p> <p>Implement <codeph>NewL()</codeph> by doing a <codeph>NewLC()</codeph>, followed by popping the pushed pointer from the cleanup stack:</p> <codeblock id="GUID-883DDCC5-8234-5319-B39E-A57C95A27882" xml:space="preserve">CCompound* CCompound::NewL(TInt aVal) 
    91     }</codeblock> <p><b>NewL()
       
    92 example</b> </p> <p>Implement <codeph>NewL()</codeph> by doing a <codeph>NewLC()</codeph>,
       
    93 followed by popping the pushed pointer from the cleanup stack:</p> <codeblock id="GUID-883DDCC5-8234-5319-B39E-A57C95A27882" xml:space="preserve">CCompound* CCompound::NewL(TInt aVal) 
    54     {
    94     {
    55     CCompound* self=NewLC(aVal);
    95     CCompound* self=NewLC(aVal);
    56     CleanupStack::Pop();
    96     CleanupStack::Pop();
    57     return self;
    97     return self;
    58     }</codeblock> <p><b>Note</b> </p> <ul><li id="GUID-E12FC4FE-9ED0-560A-9282-F8BE4238CE7D"><p>Two-stage construction for a class could be avoided by including a <codeph>CleanupStack::PushL(this)</codeph> at the start of the class's C++ constructor. This would achieve the same effect as using <codeph>ConstructL()</codeph>. 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 <codeph>NewLC()</codeph>.</p> </li> </ul> </section> </conbody></concept>
    98     }</codeblock> <p><b>Note</b> </p> <ul>
       
    99 <li id="GUID-E12FC4FE-9ED0-560A-9282-F8BE4238CE7D"><p>Two-stage construction
       
   100 for a class could be avoided by including a <codeph>CleanupStack::PushL(this)</codeph> at
       
   101 the start of the class's C++ constructor. This would achieve the same effect
       
   102 as using <codeph>ConstructL()</codeph>. However if the class is to be used
       
   103 as a base class, the constructor of any classes derived from it will incur
       
   104 the overhead of one push and pop in the constructor called at each level in
       
   105 the inheritance hierarchy, rather than one pop and push in its own <codeph>NewLC()</codeph>.</p> </li>
       
   106 </ul> </section>
       
   107 </conbody></concept>