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->ConstructL(); |
82 self->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> |