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

📄 chapter15.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution is called <I>late
binding<A NAME="Index2410"></A><A NAME="Index2411"></A></I>, which means the
binding occurs at runtime, based on the type of the object. Late binding is also
called <I>dynamic binding<A NAME="Index2412"></A><A NAME="Index2413"></A></I> or
<I>runtime binding<A NAME="Index2414"></A><A NAME="Index2415"></A></I>. When a
language implements late binding, there must be some mechanism to determine the
type of the object at runtime and call the appropriate member function. In the
case of a compiled language, the compiler still doesn&#8217;t know the actual
object type, but it inserts code that finds out and calls the correct function
body. The late-binding mechanism varies from language to language, but you can
imagine that some sort of type information must be installed in the objects.
You&#8217;ll see how this works
later.</FONT><A NAME="_Toc305593265"></A><A NAME="_Toc305628737"></A><A NAME="_Toc312374043"></A><A NAME="_Toc472655020"></A><BR></P></DIV>
<A NAME="Heading438"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
virtual functions</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To cause late binding to occur for a
particular function, C++ requires that you use the <B>virtual</B>
keyword<A NAME="Index2416"></A><A NAME="Index2417"></A> when declaring the
function in the base class. Late binding occurs only with <B>virtual</B>
functions, and only when you&#8217;re using an address of the base class where
those <B>virtual</B> functions exist, although they may also be defined in an
earlier base class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To create a member function as
<B>virtual</B>, you simply precede the declaration <A NAME="Index2418"></A>of
the function with the keyword <B>virtual</B>. Only the declaration needs the
<B>virtual</B> keyword, not the definition. If a function is declared as
<B>virtual</B> in the base class, it is <B>virtual</B> in all the derived
classes. The redefinition of a <B>virtual </B>function in a derived class is
usually called
<I>overriding<A NAME="Index2419"></A><A NAME="Index2420"></A><A NAME="Index2421"></A>.</I></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="Index2422"></A><A NAME="Index2423"></A><A NAME="Index2424"></A><FONT FACE="Georgia">Notice
that you are only required to declare a function <B>virtual</B> in the base
class. All derived-class functions that match the signature of the base-class
declaration will be called using the virtual mechanism. You <I>can</I> use the
<B>virtual</B> keyword in the derived-class declarations
<A NAME="Index2425"></A><A NAME="Index2426"></A><A NAME="Index2427"></A>(it does
no harm to do so), but it is redundant and can be confusing. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To get the desired behavior from
<B>Instrument2.cpp</B>, simply add the <B>virtual</B> keyword in the base class
before <B>play(&#160;)</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C15:Instrument3.cpp</font>
<font color=#009900>// Late binding with the virtual keyword</font>
#include &lt;iostream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>enum</font> note { middleC, Csharp, Cflat }; <font color=#009900>// Etc.</font>

<font color=#0000ff>class</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Instrument::play"</font> &lt;&lt; endl;
  }
};

<font color=#009900>// Wind objects are Instruments</font>
<font color=#009900>// because they have the same interface:</font>
<font color=#0000ff>class</font> Wind : <font color=#0000ff>public</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#009900>// Override interface function:</font>
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Wind::play"</font> &lt;&lt; endl;
  }
};

<font color=#0000ff>void</font> tune(Instrument&amp; i) {
  <font color=#009900>// ...</font>
  i.play(middleC);
}

<font color=#0000ff>int</font> main() {
  Wind flute;
  tune(flute); <font color=#009900>// Upcasting</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This file is identical to
<B>Instrument2.cpp</B> except for the addition of the <B>virtual</B> keyword,
and yet the behavior is significantly different: Now the output is
<B>Wind::play</B>.</FONT><A NAME="_Toc312374044"></A><A NAME="_Toc472655021"></A><BR></P></DIV>
<A NAME="Heading439"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Extensibility<BR><A NAME="Index2428"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">With <B>play(&#160;)</B> defined as
<B>virtual</B> in the base class, you can add as many new types as you want
without changing the <B>tune(&#160;)</B> function. In a well-designed OOP
program, most or all of your functions will follow the model of
<B>tune(&#160;)</B> and communicate only with the base-class
interface<A NAME="Index2429"></A><A NAME="Index2430"></A>. Such a program is
<I>extensible<A NAME="Index2431"></A></I> because you can add new functionality
by inheriting new data types from the common base class. The functions that
manipulate the base-class interface will not need to be changed at all to
accommodate the new classes.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s the instrument example with
more virtual functions and a number of new classes, all of which work correctly
with the old, unchanged <B>tune(&#160;)</B> function:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C15:Instrument4.cpp</font>
<font color=#009900>// Extensibility in OOP</font>
#include &lt;iostream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>enum</font> note { middleC, Csharp, Cflat }; <font color=#009900>// Etc.</font>

<font color=#0000ff>class</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Instrument::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>virtual</font> <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> {
    <font color=#0000ff>return</font> <font color=#004488>"Instrument"</font>;
  }
  <font color=#009900>// Assume this will modify the object:</font>
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> adjust(<font color=#0000ff>int</font>) {}
};

<font color=#0000ff>class</font> Wind : <font color=#0000ff>public</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Wind::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>"Wind"</font>; }
  <font color=#0000ff>void</font> adjust(<font color=#0000ff>int</font>) {}
};

<font color=#0000ff>class</font> Percussion : <font color=#0000ff>public</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Percussion::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>"Percussion"</font>; }
  <font color=#0000ff>void</font> adjust(<font color=#0000ff>int</font>) {}
};

<font color=#0000ff>class</font> Stringed : <font color=#0000ff>public</font> Instrument {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Stringed::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>"Stringed"</font>; }
  <font color=#0000ff>void</font> adjust(<font color=#0000ff>int</font>) {}
};

<font color=#0000ff>class</font> Brass : <font color=#0000ff>public</font> Wind {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Brass::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>"Brass"</font>; }
};

<font color=#0000ff>class</font> Woodwind : <font color=#0000ff>public</font> Wind {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
    cout &lt;&lt; <font color=#004488>"Woodwind::play"</font> &lt;&lt; endl;
  }
  <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>"Woodwind"</font>; }
};

<font color=#009900>// Identical function from before:</font>
<font color=#0000ff>void</font> tune(Instrument&amp; i) {
  <font color=#009900>// ...</font>
  i.play(middleC);
}

<font color=#009900>// New function:</font>
<font color=#0000ff>void</font> f(Instrument&amp; i) { i.adjust(1); }

<font color=#009900>// Upcasting during array initialization:</font>
Instrument* A[] = {
  <font color=#0000ff>new</font> Wind,
  <font color=#0000ff>new</font> Percussion,
  <font color=#0000ff>new</font> Stringed,
  <font color=#0000ff>new</font> Brass,
};

<font color=#0000ff>int</font> main() {
  Wind flute;
  Percussion drum;
  Stringed violin;
  Brass flugelhorn;
  Woodwind recorder;
  tune(flute);
  tune(drum);
  tune(violin);
  tune(flugelhorn);
  tune(recorder);
  f(flugelhorn);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that another inheritance
level has been added beneath <B>Wind</B>, but the <B>virtual</B> mechanism works
correctly no matter how many levels there are. The <B>adjust(&#160;)</B>
function is <I>not</I> overridden for <B>Brass</B> and <B>Woodwind</B>. When
this happens, the &#8220;closest&#8221; definition in the inheritance hierarchy
is automatically used &#8211; the compiler guarantees there&#8217;s always
<I>some</I> definition for a virtual function, so you&#8217;ll never end up with
a call that doesn&#8217;t bind to a function body. (That would be
disastrous.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The array <B>A[ ]</B> contains pointers
to the base class <B>Instrument</B>, so upcasting occurs during the process of
array initialization. This array and the function <B>f(&#160;)</B> will be used
in later discussions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the call to <B>tune(&#160;)</B>,
upcasting <A NAME="Index2432"></A>is performed on each different type of object,
yet the desired behavior always takes place. This can be described as
&#8220;sending a message<A NAME="Index2433"></A><A NAME="Index2434"></A> to an
object and letting the object worry about what to do with it.&#8221; The
<B>virtual</B> function is the lens to use when you&#8217;re trying to analyze a
project: Where should the base classes occur, and how might you want to extend
the program? However, even if you don&#8217;t discover the proper base class
interfaces and virtual functions at the initial creation of the program,
you&#8217;ll often discover them later, even much later, when you set out to
extend or otherwise maintain the program. This is not an analysis or design
error; it simply means you didn&#8217;t or couldn&#8217;t know all the
information the first time. Because of the tight class modularization in C++, it
isn&#8217;t a large problem when this occurs because changes you make in one

⌨️ 快捷键说明

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