⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch12.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<tt>  {</tt><tt>    Person p(c, c);</tt><tt>  }</tt><tt>  return 0;  </tt><tt>}</tt></pre><p>The two versions were compared on a Pentium II, 233MHz machine. To corroborate   the results, the test was repeated five times. When a member initialization   list was used, the <tt>for</tt> loop in <tt>main()</tt> took 12 seconds, on   average. The nonoptimized version took 15 seconds, on average. In other words,   the assignment inside the constructor body is slower by a factor of 25% compared   to the member-initialized constructor. The member function counters can give   you a clue as to the reasons for the difference. Table 12.1 presents the number   of member function calls of class <tt>C</tt> for the member initialized constructor   and for the assignment inside the constructor's body.</p><h4> Table 12.1 Comparison Between Member Initialization and Assignment Within   the Constructor's Body for Class Person </h4><table border>  <tr valign="TOP" align="left">     <td colspan=1 align="left">       <p><b>Initialization Method</b></p>    </td>    <td colspan=1 align="left">       <p><b>Default Constructor Calls</b></p>    </td>    <td colspan=1 align="left">       <p><b>Assignment Operator Calls</b></p>    </td>    <td colspan=1 align="left">       <p><b>Copy Constructor Calls</b></p>    </td>    <td colspan=1 align="left">       <p><b>Destructor Calls</b></p>    </td>  </tr>  <tr valign="TOP" align="left">     <td colspan=1 align="left">       <p>Member initialization list</p>    </td>    <td colspan=1 align="left">       <p>0</p>    </td>    <td colspan=1 align="left">       <p>0</p>    </td>    <td colspan=1 align="left">       <p>60,000,000</p>    </td>    <td colspan=1 align="left">       <p>60,000,000</p>    </td>  </tr>  <tr valign="TOP" align="left">     <td colspan=1 align="left">       <p>Assignment within Constructor</p>    </td>    <td colspan=1 align="left">       <p>60,000,000</p>    </td>    <td colspan=1 align="left">       <p>60,000,000</p>    </td>    <td colspan=1 align="left">       <p>0</p>    </td>    <td colspan=1 align="left">       <p>60,000,000</p>    </td>  </tr></table><p>When a member initialization list is used, only the copy constructor and the   destructor of the embedded object are called (note that <tt>Person</tt> has   two embedded members), whereas the assignment within the constructor body also   adds a default constructor call per embedded object. In Chapter 4, you learned   how the compiler inserts additional code into the constructor's body before   any user-written code. The additional code invokes the constructors of the base   classes and embedded objects of the class. In the case of polymorphic classes,   this code also initializes the <tt>vptr</tt>. The assigning constructor of class   <tt>Person</tt> is transformed into something such as the following:</p><pre><tt>Person::Person(const C&amp; c1, const C&amp; c2) //assignment within constructor body</tt><tt>{</tt><tt> //pseudo C++ code inserted by the compiler before user-written code</tt><tt>  c_1.C::C(); //invoke default constructor of embedded object c_1</tt><tt>  c_2.C::C(); //invoke default constructor of embedded object c_2</tt><tt>//user-written code comes here:</tt><tt>  c_1 = c1; </tt><tt>  c_2 = c2; </tt><tt>}</tt></pre><p>The default construction of the embedded objects is unnecessary because they   are reassigned new values immediately afterward. The member initialization list,   on the other hand, appears before any user-written code in the constructor.   Because the constructor body does not contain any user-written code in this   case, the transformed constructor looks similar to the following:</p><pre><tt>Person::Person(const C&amp; c1, const C&amp; c2) // member initialization list ctor</tt><tt>{</tt><tt> //pseudo C++ code inserted by the compiler before user-written code</tt><tt>  c_1.C::C(c1); //invoke copy constructor of embedded object c_1</tt><tt>  c_2.C::C(c2); //invoke copy constructor of embedded object c_2</tt><tt>//user-written code comes here (note: there's no user code)</tt><tt>}</tt></pre><p>You can conclude from this example that for a class that has subobjects, a   member initialization list is preferable to an assignment within the constructor's   body. For this reason, many programmers use member initialization lists across   the board, even for data members of fundamental types.</p><h3> <a name="Heading8"> Prefix Versus Postfix Operators </a></h3><p>The prefix operators <tt>++</tt> and <tt>--</tt> tend to be more efficient   than their postfix versions because when postfix operators are used, a temporary   copy is needed to retain the value of the operand before it is changed. For   fundamental types, the compiler can eliminate the extra copy. However, for user-defined   types, this is nearly impossible. A typical implementation of the overloaded   prefix and postfix operators demonstrates the difference between the two: </p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt>  //...</tt><tt>  int AddDays(int d); </tt><tt>public:</tt><tt>  Date operator++(int unused); </tt><tt>  Date&amp; operator++(); </tt><tt>};</tt><tt>Date Date::operator++(int unused)  //postfix</tt><tt>{</tt><tt>  Date temp(*this); //create a copy of the current object</tt><tt>  this-&gt;AddDays(1); //increment  current object</tt><tt>  return temp; //return by value a copy of the object before it was incremented</tt><tt>}</tt><tt>Date&amp; Date::operator++()   //prefix</tt><tt>{ </tt><tt>  this-&gt;AddDays(1); //increment  current object</tt><tt>  return *this; //return by reference the current object</tt><tt>}</tt></pre><p>The overloaded postfix <tt>++</tt> is significantly less efficient than the   prefix for two reasons: It requires the creation of a temporary copy, and it   returns that copy by value. Therefore, whenever you are free to choose between   postfix and prefix operators of an object, choose the prefix version.</p><h2> <a name="Heading9"> Inline Functions</a></h2><p>Inline functions can eliminate the overhead incurred by a function call and   still provide the advantages of ordinary functions. However, inlining is not   a panacea. In some situations, it can even degrade the program's performance.   It is important to use this feature judiciously.</p><h3> <a name="Heading10"> Function Call Overhead</a></h3><p>The exact cost of an ordinary function call is implementation-dependent. It   usually involves storing the current stack state, pushing the arguments of the   function onto the stack and initializing them, and jumping to the memory address   that contains the function's instructions -- only then does the function begin   to execute. When the function returns, a sequence of reverse operations also   takes place. In other languages (such as Pascal and COBOL), the overhead of   a function call is even more noticeable because there are additional operations   that the implementation performs before and after a function call. For a member   function that merely returns the value of a data member, this overhead can be   unacceptable.<b> </b>Inline functions were added to C++ to allow efficient implementation   of such accessor and mutator member functions (<i>getters</i> and <i>setters</i>,   respectively). Nonmember functions can also be declared <tt>inline</tt>. </p><h3> <a name="Heading11"> Benefits of Inline Functions</a></h3><p>The benefits of inlining a function are significant: From a user's point of   view, the inlined function looks like an ordinary function. It can have arguments   and a return value; furthermore, it has its own scope, yet it does not incur   the overhead of a full-blown function call. In addition, it is remarkably safer   and easier to debug than using a macro. But there are even more benefits. When   the body of a function is inlined, the compiler can optimize the resultant code   even further by applying context-specific optimizations that it cannot perform   on the function's code alone. </p><p>All member functions that are implemented inside the class body are implicitly   declared <tt>inline</tt>. In addition, compiler synthesized constructors, copy   constructors, assignment operators, and destructors are implicitly declared   <tt>inline</tt>. For example</p><pre><tt>class A </tt><tt>{</tt><tt>private:</tt><tt>  int a;</tt><tt>public:</tt><tt>  int Get_a() { return a; } // implicitly inline</tt><tt>  virtual void Set_a(int aa) { a = aa; } //implicitly inline</tt><tt>  //compiler synthesized canonical member functions also declared inline</tt><tt>};</tt></pre><p>It is important to realize, however, that the <tt>inline</tt> specifier is   merely a recommendation to the compiler. The compiler is free to ignore this   recommendation and <i>outline</i> the function; it can also inline a function   that was not explicitly declared <tt>inline</tt>. Fortunately, C++ guarantees   that the function's semantics cannot be altered by the compiler just because   it is or is not inlined. For example, it is possible to take the address of   a function that was not declared <tt>inline</tt>, regardless of whether it was   inlined by the compiler (the result, however, is the creation of an outline   copy of the function). How do compilers determine which functions are to be   inlined and which are not? They have proprietary heuristics that are designed   to pick the best candidates for inlining, depending on various criteria. These   criteria include the size of the function body, whether it declares local variables,   its complexity (for example, recursion and loops usually disqualify a function   from inlining), and additional implementation- and context-dependent factors.</p><h3> <a name="Heading12"> What Happens When a Function that Is Declared inline   Cannot Be Inlined?</a></h3><p>Theoretically, when the compiler refuses to inline a function, that function   is then treated like an ordinary function: The compiler generates the object   code for it, and invocations of the function are transformed into a jump to   its memory address. Unfortunately, the implications of outlining a function   are more complicated than that. It is a common practice to define inline functions   in the class declaration. For example</p><pre><tt>   // filename Time.h</tt><tt>#include&lt;ctime&gt;</tt><tt>#include&lt;iostream&gt;</tt><tt>using namespace std;</tt><tt>class Time</tt><tt>{</tt><tt>public:</tt><tt>  inline void Show() { for (int i = 0; i&lt;10; i++) cout&lt;&lt;time(0)&lt;&lt;endl;}</tt><tt>};</tt><tt>   // filename Time.h</tt></pre><p>Because the member function <tt>Time::Show()</tt> contains a local variable   and a <tt>for</tt> loop, the compiler is likely to ignore the <tt>inline</tt>   request and treat it as an ordinary member function. However, the class declaration   itself can be <tt>#included</tt> in separately compiled translation units:</p><pre><tt>    // filename f1.cpp</tt><tt>#include "Time.hj"</tt><tt>void f1()</tt><tt>{</tt><tt>  Time t1;</tt><tt>  t1.Show();</tt><tt>}</tt><tt>    // f1.cpp</tt><tt>// filename f2.cpp</tt><tt>#include "Time.h"</tt><tt>void f2()</tt><tt>{</tt><tt>  Time t2;</tt><tt>  t2.Show();</tt><tt>}</tt><tt>    // f2.cpp</tt></pre><p>As a result, the compiler generates two identical copies of the same member   function for the same program: </p><pre><tt>void f1();</tt><tt>void f2();</tt><tt>int main()</tt><tt>{</tt><tt>  f1(); </tt><tt>  f2();</tt><tt>  return 0;</tt>

⌨️ 快捷键说明

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