📄 chapter15.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIC2Vone.rtf
Application Directory:C:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:09/27/2001
Translation Time:05:25:57
Translation Platform:Win32
Number of Output files:22
This File:Chapter15.html
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>15: Polymorphism & Virtual Functions</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview-head.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana" size = "-1">
[ <a href="README-HTML.txt">Viewing Hints</a> ]
[ <a href="http://www.mindview.net/CPPServices/SolutionGuide.html">Exercise Solutions</a> ]
[ <a href="http://www.mindview.net/ThinkingInCPP2e.html">Volume 2</a> ]
[ <a href="http://www.mindview.net/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/CPPServices/#PublicSeminars">Seminars</a> ]
[ <a href="http://www.mindview.net/CPPServices/#SeminarsOnCD">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/CPPServices/#ConsultingServices">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana">
Thinking in C++, 2nd ed. Volume 1</FONT></H2></FONT>
<H3><FONT FACE="Verdana">©2000 by Bruce Eckel</FONT></H3></FONT>
<FONT FACE="Verdana" size = "-1">
[ <a href="Chapter14.html">Previous Chapter</a> ]
[ <a href="Contents.html">Table of Contents</a> ]
[ <a href="DocIndex.html">Index</a> ]
[ <a href="Chapter16.html">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="_Toc472655015"></A><A NAME="Heading433"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
15: Polymorphism & <BR>Virtual Functions <A NAME="Index2390"></A></H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>Polymorphism (implemented in C++
with
<A NAME="Index2391"></A><A NAME="Index2392"></A><A NAME="Index2393"></A><B>virtual</B>
functions) is the third essential feature of an object-oriented programming
language, after data abstraction and inheritance. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It provides another dimension of
separation of interface from implementation, to decouple <I>what</I> from
<I>how</I>. Polymorphism allows improved code organization and readability as
well as the creation of <I>extensible</I> programs that can be
“grown” not only during the original creation of the project, but
also when new features are desired.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Encapsulation creates new data types by
combining characteristics and behaviors. Access control separates the interface
from the implementation by making the details <B>private</B>. This kind of
mechanical organization makes ready sense to someone with a procedural
programming background. But virtual functions deal with
<A NAME="Index2394"></A>decoupling in terms of <I>types</I>. In Chapter 14, you
saw how inheritance allows the treatment of an object as its own type <I>or</I>
its base type. This ability is critical because it allows many types (derived
from the same base type) to be treated as if they were one type, and a single
piece of code to work on all those different types equally. The virtual function
allows one type to express its distinction from another, similar type, as long
as they’re both derived from the same base type. This distinction is
expressed through differences in behavior of the functions that you can call
through the base class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In this chapter, you’ll learn about
virtual functions, starting from the basics with simple examples that strip away
everything but the “virtualness” of the
program.</FONT><A NAME="_Toc312374039"></A><A NAME="_Toc472655016"></A><BR></P></DIV>
<A NAME="Heading434"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Evolution of C++ programmers</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">C
programmers<A NAME="Index2395"></A><A NAME="Index2396"></A> seem to acquire C++
in three steps. First, as simply a “better C,” because C++ forces
you to declare all functions before using them and is much pickier about how
variables are used. You can often find the errors in a C program simply by
compiling it with a C++ compiler.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The second step is
“object-based” C++<A NAME="Index2397"></A><A NAME="Index2398"></A>.
This means that you easily see the code organization benefits of grouping a data
structure together with the functions that act upon it, the value of
constructors and destructors, and perhaps some simple inheritance. Most
programmers who have been working with C for a while quickly see the usefulness
of this because, whenever they create a library, this is exactly what they try
to do. With C++, you have the aid of the compiler.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can get stuck at the object-based
level because you can quickly get there and you get a lot of benefit without
much mental effort. It’s also easy to feel like you’re creating data
types – you make classes and objects, you send messages to those objects,
and everything is nice and neat.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But don’t be fooled. If you stop
here, you’re missing out on the greatest part of the language, which is
the jump to true object-oriented programming. You can do this only with virtual
functions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Virtual
functions<A NAME="Index2399"></A><A NAME="Index2400"></A> enhance the concept of
type instead of just encapsulating code inside structures and behind walls, so
they are without a doubt the most difficult concept for the new C++ programmer
to fathom. However, they’re also the turning point in the understanding of
object-oriented programming. If you don’t use virtual functions, you
don’t understand OOP yet.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the virtual function is
intimately bound with the concept of type, and type is at the core of
object-oriented programming, there is no analog to the virtual function in a
traditional procedural language. As a procedural programmer, you have no
referent with which to think about virtual functions, as you do with almost
every other feature in the language. Features in a procedural language can be
understood on an algorithmic level, but virtual functions can be understood only
from a design
viewpoint.</FONT><A NAME="_Toc305593263"></A><A NAME="_Toc305628735"></A><A NAME="_Toc312374040"></A><A NAME="_Toc472655017"></A><BR></P></DIV>
<A NAME="Heading435"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Upcasting</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In Chapter 14 you saw how an object can
be used as its own type or as an object of its base type. In addition, it can be
manipulated through an address of the base type. Taking the address of an object
(either a pointer or a reference) and treating it as the address of the base
type is called <I>upcasting<A NAME="Index2401"></A></I> because of the way
inheritance trees are drawn with the base class at the top.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You also saw a problem arise, which is
embodied in the following code:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C15:Instrument2.cpp</font>
<font color=#009900>// Inheritance & upcasting</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>enum</font> note { middleC, Csharp, Eflat }; <font color=#009900>// Etc.</font>
<font color=#0000ff>class</font> Instrument {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
cout << <font color=#004488>"Instrument::play"</font> << 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>// Redefine interface function:</font>
<font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> {
cout << <font color=#004488>"Wind::play"</font> << endl;
}
};
<font color=#0000ff>void</font> tune(Instrument& 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">The function <B>tune( )</B> accepts
(by <A NAME="Index2402"></A><A NAME="Index2403"></A>reference) an
<B>Instrument</B>, but also without complaint anything derived from
<B>Instrument</B>. In <B>main( )</B>, you can see this happening as a
<B>Wind</B> object is passed to <B>tune( )</B>, with no
<A NAME="Index2404"></A>cast necessary. This is acceptable; the interface in
<B>Instrument</B> must exist in <B>Wind</B>, because <B>Wind</B> is publicly
inherited from <B>Instrument</B>. Upcasting from <B>Wind</B> to
<B>Instrument</B> may “narrow” that interface, but never less than
the full interface to <B>Instrument</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="Index2405"></A><A NAME="Index2406"></A><FONT FACE="Georgia">The
same arguments are true when dealing with pointers; the only difference is that
the user must explicitly take the addresses of objects as they are passed into
the
function.</FONT><A NAME="_Toc305593264"></A><A NAME="_Toc305628736"></A><A NAME="_Toc312374041"></A><A NAME="_Toc472655018"></A><BR></P></DIV>
<A NAME="Heading436"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The problem</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The problem with <B>Instrument2.cpp</B>
can be seen by running the program. The output is <B>Instrument::play</B>. This
is clearly not the desired output, because you happen to know that the object is
actually a <B>Wind</B> and not just an <B>Instrument</B>. The call should
produce <B>Wind::play</B>. For that matter, any object of a class derived from
<B>Instrument</B> should have its version of <B>play( )</B> used,
regardless of the situation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The behavior of <B>Instrument2.cpp</B> is
not surprising, given C’s approach to functions. To understand the issues,
you need to be aware of the concept of
<I>binding</I>.</FONT><A NAME="_Toc312374042"></A><A NAME="_Toc472655019"></A><BR></P></DIV>
<A NAME="Heading437"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Function call binding<BR><A NAME="Index2407"></A><A NAME="Index2408"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Connecting a function call to a function
body is called <I>binding</I>. When binding is performed before the program is
run (by the compiler and linker), it’s called <I>early
binding<A NAME="Index2409"></A></I>. You may not have heard the term before
because it’s never been an option with procedural languages: C compilers
have only one kind of function call, and that’s early
binding.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The problem in the program above is
caused by early binding because the compiler cannot know the correct function to
call when it has only an <B>Instrument</B> address.</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -