wwwtc1answer.htm

来自「C++builder学习资料C++builder」· HTM 代码 · 共 203 行

HTM
203
字号
<HTML>

<HEAD>

<TITLE>Answer What's wrong with this code? Volume #1</TITLE>

<META content="text/html; charset=gb2312" http-equiv=Content-Type>

<META content="Harold Howe" name=Author>

<META content="Microsoft FrontPage 4.0" name=GENERATOR></HEAD>

<BODY>

<CENTER>

<TABLE border=0 cellPadding=0 cellSpacing=0 width=640>

  <TBODY>

  <TR>

    <TD>

      <H2>Answer What's wrong with this code? Volume #1 </H2> 

      <H4>The assignment operator </H4> 

      <P>The assignment operator for the <TT>Foo</TT> class contains a common  

      programming mistake. Assignment operators should almost always pass their  

      arguments by const reference. A lot of programmers leave out the const  

      part, and just pass by reference. When you forget the const keyword, the  

      code will usually continue to compile and run correctly. However, the code  

      example in this issue demonstrates a situation where const cannot be  

      omitted. Recall that the original assignment operator looked like this:  

      </P><PRE>Foo<B>&amp;</B> <B>operator</B><B>=</B><B>(</B> Foo <B>&amp;</B>rhs<B>)</B>

<B>{</B>

    cout <B>&lt;&lt;</B> <FONT color=blue>&quot;assignment&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

    <B>return</B> <B>*</B><B>this</B><B>;</B>

<B>}</B>

</PRE> 

      <P>The compilation error occurred where an object was being assigned a  

      return value from a function. </P><PRE>foo1 <B>=</B> GetAFoo<B>(</B><B>)</B><B>;</B>   <FONT color=navy>// generates compiler error</FONT>

</PRE> 

      <P>So why does this assignment fail to compile, when the assignment to  

      <TT>foo2</TT> compiles without error? </P><PRE>foo2 <B>=</B> foo1<B>;</B>        <FONT color=navy>// compiles OK</FONT>

</PRE> 

      <P>The difference is that <TT>GetAFoo</TT> returns a <TT>Foo</TT> object  

      by value. Return objects are temporary objects, and temporary objects are  

      const objects. You cannot modify a temporary object. <TT>foo1</TT> on the  

      other hand, is not a temporary object, which means that it is not a const  

      object. </P> 

      <P>The assignment operator for <TT>Foo</TT> takes a non-const object by  

      reference. The compiler will not pass const objects to a function that  

      take references to non-const objects. Why? Because when you pass a value  

      by reference, the function has the power to alter the original object. The  

      compiler must ensure that const objects are not changed. </P> 

      <P>Because the foo assignment operator did not list the <TT>const</TT>  

      keyword, the compiler will not try to use it for temporary objects. Since  

      no other suitable assignment operator is available that takes const  

      parameters, the compiler issues an error. </P> 

      <P>The solution to this problem is to change the assignment operator so it  

      takes a constant reference. </P><PRE><B>class</B> Foo

<B>{</B>

<B>public</B><B>:</B>

    <B>...</B>

    Foo<B>&amp;</B> <B>operator</B><B>=</B><B>(</B><B>const</B> Foo <B>&amp;</B>rhs<B>)</B>

    <B>{</B>

        cout <B>&lt;&lt;</B> <FONT color=blue>&quot;assignment&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

        <B>return</B> <B>*</B><B>this</B><B>;</B>

    <B>}</B>

<B>}</B><B>;</B>

</PRE> 

      <P>Once this change is made to the <TT>Foo</TT> class, the code example  

      compiles and runs without error. Here is the output of the program after  

      making this correction. Note that the comments were added to explain the  

      output. </P><PRE>default constructor         // construct foo1

default constructor         // construct foo2

default constructor         // construct foo inside GetAFoo

copy construction           // copy foo to temporary return object

destructor                  // destory local foo in GetAFoo

assignment                  // assign temp return object to foo1

destructor                  // destory temp object

assignment                  // assign foo1 to foo2

destructor                  // end of main, destroy foo1

destructor                  // and destroy foo2.

</PRE> 

      <P>In addition to fixing the compilation error, there are a couple of ways  

      to streamline the code. The first change involves the <TT>GetAFoo</TT>  

      function, which originally looked like this: </P><PRE>Foo GetAFoo<B>(</B><B>)</B>

<B>{</B>

    Foo foo<B>;</B>

    <B>return</B> foo<B>;</B>

<B>}</B>

</PRE> 

      <P>As it stands, this code will construct two <TT>Foo</TT> objects. The  

      first is the <TT>foo</TT> variable that you see in the code. The second  

      object is the return object for the function. The <TT>foo</TT> variable is  

      constructed with the default constructor for the <TT>Foo</TT> class. The  

      temporary return object is created with the copy constructor. The debug  

      statements from the program shows how and when these two objects are  

      created and destroyed.  

      <P><PRE>...

default constructor  // creates foo variable

copy construction    // construct temp return object

destructor           // destroy foo because it is out of scope

assignment           // call assignment operator and pass temp object

destructor           // delete temp object

...

</PRE> 

      <P>As this output shows, calling the <TT>GetAFoo</TT> function results in  

      the construction and deletion of two <TT>Foo</TT> objects. Because  

      <TT>GetAFoo</TT> does not use the local <TT>foo</TT> variable for  

      anything, we can streamline the function by eliminating the variable. The  

      new and improved <TT>GetAFoo</TT> function looks like this: </P><PRE>Foo GetAFoo<B>(</B><B>)</B>

<B>{</B>

    <B>return</B> Foo<B>(</B><B>)</B><B>;</B>

<B>}</B>

</PRE> 

      <P> 

      <TABLE width="75%"> 

        <TBODY> 

        <TR> 

          <TD vAlign=top><IMG align=top alt=Note border=0 hspace=0  

            src="images/exclamation.gif" width="32" height="48"> </TD> 

          <TD vAlign=top><B>Note:</B>  

            <HR SIZE=1> 

            This optimization is called the <I>Return Value Optimization</I>.  

            Compilers do not have to provide this optimization, but most of them  

            do. All versions of C++Builder implement this optimization. Scott  

            Meyers discusses this optimization technique in his book <A  

            href="http://www.amazon.com/exec/obidos/ISBN=020163371X/cbuilderfoundatiA/"  

            target=_top>More Effective C++</A>.  

            <HR SIZE=1> 

          </TD></TR></TBODY></TABLE> 

      <P>This code eliminates the temporary variable, the copy constructor that  

      was needed to initialize it, and the destructor that was needed to  

      deallocate it. The debug output from the streamlined function looks like  

      this. </P><PRE>...

default constructor   // create return object with default constructor

assignment            // pass return object to assignment operator

destructor            // destroy return object

...

</PRE> 

      <P>There is one other place where the code can be improved. Notice how the  

      <TT>main</TT> function creates and initializes the two <TT>Foo</TT>  

      objects. </P><PRE><B>int</B> main<B>(</B><B>)</B>

<B>{</B>

    Foo foo1<B>;</B>           <FONT color=navy>// default constructor</FONT>

    Foo foo2<B>;</B>           <FONT color=navy>// default constructor</FONT>

    foo1 <B>=</B> GetAFoo<B>(</B><B>)</B><B>;</B>   <FONT color=navy>// Assignment</FONT>

    foo2 <B>=</B> foo1<B>;</B>        <FONT color=navy>// Assignment</FONT>

    <B>return</B> <FONT color=blue>0</FONT><B>;</B>

<B>}</B>

</PRE> 

      <P>When <TT>main</TT> is structured like this, four function calls are  

      needed to initialize two objects. When can reduce this to two calls if by  

      eliminating the use of the default constructors. </P><PRE><B>int</B> main<B>(</B><B>)</B>

<B>{</B>

    Foo foo1<B>(</B>GetAFoo<B>(</B><B>)</B><B>)</B><B>;</B> <FONT color=navy>// copy constructor</FONT>

    Foo foo2<B>(</B>foo1<B>)</B><B>;</B>      <FONT color=navy>// copy constructor</FONT>

    <B>return</B> <FONT color=blue>0</FONT><B>;</B>

<B>}</B>

</PRE> 

      <P>Ironically, this code enhancement eliminates the use of the assignment  

      operator, and the assignment operator was the original cause of our  

      compiler errors. After making these improvements to the code, the final  

      source should look like this: </P><PRE><FONT color=green>#include &lt;iostream&gt;</FONT>



<B>using</B> <B>namespace</B> std<B>;</B>



<B>class</B> Foo

<B>{</B>

<B>public</B><B>:</B>

    Foo<B>(</B><B>)</B>

    <B>{</B>

        cout <B>&lt;&lt;</B> <FONT color=blue>&quot;default constructor&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

    <B>}</B>

    Foo<B>(</B><B>const</B> Foo <B>&amp;</B><B>)</B>

    <B>{</B>

        cout <B>&lt;&lt;</B> <FONT color=blue>&quot;copy construction&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

    <B>}</B>



    <B>~</B>Foo<B>(</B><B>)</B>

    <B>{</B>

        cout <B>&lt;&lt;</B> <FONT color=blue>&quot;destructor&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

    <B>}</B>



    Foo<B>&amp;</B> <B>operator</B><B>=</B><B>(</B><B>const</B> Foo <B>&amp;</B>rhs<B>)</B>

    <B>{</B>

        cout <B>&lt;&lt;</B> <FONT color=blue>&quot;assignment&quot;</FONT> <B>&lt;&lt;</B> endl<B>;</B>

        <B>return</B> <B>*</B><B>this</B><B>;</B>

    <B>}</B>

<B>}</B><B>;</B>





Foo GetAFoo<B>(</B><B>)</B>

<B>{</B>

    <B>return</B> Foo<B>(</B><B>)</B><B>;</B>

<B>}</B>



<B>int</B> main<B>(</B><B>)</B>

<B>{</B>

    Foo foo1<B>(</B>GetAFoo<B>(</B><B>)</B><B>)</B><B>;</B>

    Foo foo2<B>(</B>foo1<B>)</B><B>;</B>

    <B>return</B> <FONT color=blue>0</FONT><B>;</B>

<B>}</B>

</PRE> 

      <P>That does it for this edition of "What's wrong with this code?" Unless  

      of course, you can find more ways to improve the final version of the  

      code. Since this is the first edition of w3TC, I would be interested in  

      your feedback. Do you like these sorts of articles, or did this article  

      suck as much as the code that was in it? Feedback is welcome. If you have  

      ideas for future articles, I would be happy to hear them. Just click the  

      email link at the bottom of the page.  

</P></TD></TR></TBODY></TABLE></CENTER></BODY></HTML> 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?