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

📄 chapter14.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<font color=#0000ff>public</font>:
  Derived1(<font color=#0000ff>int</font>) : m2(1), m1(2), Base1(3) {
    out &lt;&lt; <font color=#004488>"Derived1 constructor\n"</font>;
  }
  ~Derived1() {
    out &lt;&lt; <font color=#004488>"Derived1 destructor\n"</font>;
  }
};

<font color=#0000ff>class</font> Derived2 : <font color=#0000ff>public</font> Derived1 {
  Member3 m3;
  Member4 m4;
<font color=#0000ff>public</font>:
  Derived2() : m3(1), Derived1(2), m4(3) {
    out &lt;&lt; <font color=#004488>"Derived2 constructor\n"</font>;
  }
  ~Derived2() {
    out &lt;&lt; <font color=#004488>"Derived2 destructor\n"</font>;
  }
};

<font color=#0000ff>int</font> main() {
  Derived2 d2;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">First, an
<A NAME="Index2309"></A><B>ofstream</B> object is created to send all the output
to a file. Then, to save some typing and demonstrate a macro technique that will
be replaced by a much improved technique in Chapter 16, a
<A NAME="Index2310"></A><A NAME="Index2311"></A>macro is created to build some
of the classes, which are then used in inheritance and composition. Each of the
constructors and destructors report themselves to the trace file. Note that the
constructors are not default constructors; they each have an <B>int</B>
argument. The argument itself has no identifier; its only reason for existence
is to force you to explicitly call the constructors in the initializer list.
(Eliminating the identifier prevents compiler warning
messages.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The output of this program
is</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Base1 constructor
Member1 constructor
Member2 constructor
Derived1 constructor
Member3 constructor
Member4 constructor
Derived2 constructor
Derived2 destructor
Member4 destructor
Member3 destructor
Derived1 destructor
Member2 destructor
Member1 destructor
Base1 destructor</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that construction starts at
the very root of the class hierarchy, and that at each level the base class
constructor is called first, followed by the member object constructors. The
destructors are called in exactly the reverse order of the constructors &#8211;
this is important because of potential dependencies (in the derived-class
constructor or destructor, you must be able to assume that the base-class
subobject is still available for use, and has already been constructed &#8211;
or not destroyed yet).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s also interesting that the
order of constructor calls for member objects is completely unaffected by the
order of the calls in the constructor initializer list. The order is determined
by the order that the member objects are declared in the class. If you could
change the order of constructor calls via the constructor initializer list, you
could have two different call sequences in two different constructors, but the
poor destructor wouldn&#8217;t know how to properly reverse the order of the
calls for destruction, and you could end up with a dependency
problem.</FONT><A NAME="_Toc312374021"></A><A NAME="_Toc472654996"></A><BR></P></DIV>
<A NAME="Heading413"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Name hiding<BR><A NAME="Index2312"></A><A NAME="Index2313"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you inherit a class and provide a new
definition for one of its member functions, there are two possibilities. The
first is that you provide the exact signature and return type in the derived
class definition as in the base class definition. This is called
<A NAME="Index2314"></A><I>redefining </I>for ordinary member functions and
<A NAME="Index2315"></A><I>overriding</I> when the base class member function is
a <A NAME="Index2316"></A><A NAME="Index2317"></A><B>virtual</B> function
(<B>virtual</B> functions are the normal case, and will be covered in detail in
Chapter 15). But what happens if you change the member function argument list or
return type in the derived class? Here&#8217;s an example:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:NameHiding.cpp</font>
<font color=#009900>// Hiding overloaded names during inheritance</font>
#include &lt;iostream&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>class</font> Base {
<font color=#0000ff>public</font>:
  <font color=#0000ff>int</font> f() <font color=#0000ff>const</font> { 
    cout &lt;&lt; <font color=#004488>"Base::f()\n"</font>; 
    <font color=#0000ff>return</font> 1; 
  }
  <font color=#0000ff>int</font> f(string) <font color=#0000ff>const</font> { <font color=#0000ff>return</font> 1; }
  <font color=#0000ff>void</font> g() {}
};

<font color=#0000ff>class</font> Derived1 : <font color=#0000ff>public</font> Base {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> g() <font color=#0000ff>const</font> {}
};

<font color=#0000ff>class</font> Derived2 : <font color=#0000ff>public</font> Base {
<font color=#0000ff>public</font>:
  <font color=#009900>// Redefinition:</font>
  <font color=#0000ff>int</font> f() <font color=#0000ff>const</font> { 
    cout &lt;&lt; <font color=#004488>"Derived2::f()\n"</font>; 
    <font color=#0000ff>return</font> 2;
  }
};

<font color=#0000ff>class</font> Derived3 : <font color=#0000ff>public</font> Base {
<font color=#0000ff>public</font>:
  <font color=#009900>// Change return type:</font>
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> { cout &lt;&lt; <font color=#004488>"Derived3::f()\n"</font>; }
};

<font color=#0000ff>class</font> Derived4 : <font color=#0000ff>public</font> Base {
<font color=#0000ff>public</font>:
  <font color=#009900>// Change argument list:</font>
  <font color=#0000ff>int</font> f(<font color=#0000ff>int</font>) <font color=#0000ff>const</font> { 
    cout &lt;&lt; <font color=#004488>"Derived4::f()\n"</font>; 
    <font color=#0000ff>return</font> 4; 
  }
};

<font color=#0000ff>int</font> main() {
  string s(<font color=#004488>"hello"</font>);
  Derived1 d1;
  <font color=#0000ff>int</font> x = d1.f();
  d1.f(s);
  Derived2 d2;
  x = d2.f();
<font color=#009900>//!  d2.f(s); // string version hidden</font>
  Derived3 d3;
<font color=#009900>//!  x = d3.f(); // return int version hidden</font>
  Derived4 d4;
<font color=#009900>//!  x = d4.f(); // f() version hidden</font>
  x = d4.f(1);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>Base</B> you see an overloaded
function <B>f(&#160;)</B>, and <B>Derived1</B> doesn&#8217;t make any changes to
<B>f(&#160;)</B> but it does redefine <B>g(&#160;)</B>. In <B>main(&#160;)</B>,
you can see that both overloaded versions of <B>f(&#160;)</B> are available in
<B>Derived1</B>. However, <B>Derived2</B> redefines one overloaded version of
<B>f(&#160;)</B> but not the other, and the result is that the second overloaded
form is unavailable. In <B>Derived3</B>, changing the return type hides both the
base class versions, and <B>Derived4</B> shows that changing the argument list
also hides both the base class versions. In general, we can say that anytime you
redefine an overloaded function name from the base class, all the other versions
are automatically hidden in the new class. In Chapter 15, you&#8217;ll see that
the addition of the <B>virtual</B> keyword affects function overloading a bit
more.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you change the interface of the base
class by modifying the
<A NAME="Index2318"></A><A NAME="Index2319"></A><A NAME="Index2320"></A>signature
and/or
<A NAME="Index2321"></A><A NAME="Index2322"></A><A NAME="Index2323"></A>return
type of a member function from the base class, then you&#8217;re using the class
in a different way than inheritance is normally intended to support. It
doesn&#8217;t necessarily mean you&#8217;re doing it wrong, it&#8217;s just that
the ultimate goal of inheritance is to support
<A NAME="Index2324"></A><I>polymorphism</I>, and if you change the function
signature or return type then you are actually changing the interface of the
base class. If this is what you have intended to do then you are using
inheritance primarily to reuse code, and not to maintain the common interface of
the base class (which is an essential aspect of polymorphism). In general, when
you use inheritance this way it means you&#8217;re taking a general-purpose
class and specializing it for a particular need &#8211; which is usually, but
not always, considered the realm of
composition.</FONT><A NAME="BBB"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For example, consider the <B>Stack</B>
class from Chapter 9. One of the problems with that class is that you had to
perform a cast every time you fetched a pointer from the container. This is not
only tedious, it&#8217;s unsafe &#8211; you could cast the pointer to anything
you want.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An approach that seems better at first
glance is to specialize the general <B>Stack</B> class using inheritance.
Here&#8217;s an example that uses the class from Chapter 9:
<A NAME="Index2325"></A></FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:InheritStack.cpp</font>
<font color=#009900>// Specializing the Stack class</font>
#include <font color=#004488>"../C09/Stack4.h"</font>
#include <font color=#004488>"../require.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>class</font> StringStack : <font color=#0000ff>public</font> Stack {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> push(string* str) {
    Stack::push(str);
  }
  string* peek() <font color=#0000ff>const</font> {
    <font color=#0000ff>return</font> (string*)Stack::peek();
  }
  string* pop() {
    <font color=#0000ff>return</font> (string*)Stack::pop();
  }
  ~StringStack() {
    string* top = pop();
    <font color=#0000ff>while</font>(top) {
      <font color=#0000ff>delete</font> top;
      top = pop();
    }
  }
};

<font color=#0000ff>int</font> main() {
  ifstream in(<font color=#004488>"InheritStack.cpp"</font>);
  assure(in, <font color=#004488>"InheritStack.cpp"</font>);
  string line;
  StringStack textlines;
  <font color=#0000ff>while</font>(getline(in, line))
    textlines.push(<font color=#0000ff>new</font> string(line));
  string* s;
  <font color=#0000ff>while</font>((s = textlines.pop()) != 0) { <font color=#009900>// No cast!</font>
    cout &lt;&lt; *s &lt;&lt; endl;
    <font color=#0000ff>delete</font> s;
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since all of the member functions in
<B>Stack4.h </B>are inlines, nothing needs to be linked.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>StringStack</B> specializes
<B>Stack</B> so that <B>push(&#160;)</B> will accept only <B>String</B>

⌨️ 快捷键说明

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