📄 chapter15.html
字号:
<font color=#009900>// Pure virtual functions:</font>
<font color=#0000ff>virtual</font> <font color=#0000ff>void</font> play(note) <font color=#0000ff>const</font> = 0;
<font color=#0000ff>virtual</font> <font color=#0000ff>char</font>* what() <font color=#0000ff>const</font> = 0;
<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>) = 0;
};
<font color=#009900>// Rest of the file is the same ...</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 << <font color=#004488>"Wind::play"</font> << 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 << <font color=#004488>"Percussion::play"</font> << 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 << <font color=#004488>"Stringed::play"</font> << 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 << <font color=#004488>"Brass::play"</font> << 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 << <font color=#004488>"Woodwind::play"</font> << 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& i) {
<font color=#009900>// ...</font>
i.play(middleC);
}
<font color=#009900>// New function:</font>
<font color=#0000ff>void</font> f(Instrument& i) { i.adjust(1); }
<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">Pure virtual functions are helpful
because they make explicit the abstractness of a class and tell both the user
and the compiler how it was intended to be used.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Note that pure virtual functions prevent
an abstract class from being passed into a function <I>by value</I>. Thus, it is
also a way to prevent <I>object slicing</I> (which will be described
shortly)<A NAME="Index2486"></A>. By making a class abstract, you can ensure
that a pointer or reference is always used during upcasting to that
class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Just because one pure virtual function
prevents the VTABLE from being completed doesn’t mean that you don’t
want function bodies for some of the others. Often you will want to call a
base-class version of a function, even if it is virtual. It’s always a
good idea to put common code as close as possible to the root of your hierarchy.
Not only does this save code space, it allows easy propagation of
changes.</FONT><A NAME="_Toc312374053"></A><A NAME="_Toc472655030"></A><BR></P></DIV>
<A NAME="Heading448"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Pure virtual
definitions<BR><A NAME="Index2487"></A><A NAME="Index2488"></A><A NAME="Index2489"></A><A NAME="Index2490"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s possible to provide a
definition for a pure virtual function in the base class. You’re still
telling the compiler not to allow objects of that abstract base class, and the
pure virtual functions must still be defined in derived classes in order to
create objects. However, there may be a common piece of code that you want some
or all of the derived class definitions to call rather than duplicating that
code in every function. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s what a pure virtual
definition looks like:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C15:PureVirtualDefinitions.cpp</font>
<font color=#009900>// Pure virtual base definitions</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Pet {
<font color=#0000ff>public</font>:
<font color=#0000ff>virtual</font> <font color=#0000ff>void</font> speak() <font color=#0000ff>const</font> = 0;
<font color=#0000ff>virtual</font> <font color=#0000ff>void</font> eat() <font color=#0000ff>const</font> = 0;
<font color=#009900>// Inline pure virtual definitions illegal:</font>
<font color=#009900>//! virtual void sleep() const = 0 {}</font>
};
<font color=#009900>// OK, not defined inline</font>
<font color=#0000ff>void</font> Pet::eat() <font color=#0000ff>const</font> {
cout << <font color=#004488>"Pet::eat()"</font> << endl;
}
<font color=#0000ff>void</font> Pet::speak() <font color=#0000ff>const</font> {
cout << <font color=#004488>"Pet::speak()"</font> << endl;
}
<font color=#0000ff>class</font> Dog : <font color=#0000ff>public</font> Pet {
<font color=#0000ff>public</font>:
<font color=#009900>// Use the common Pet code:</font>
<font color=#0000ff>void</font> speak() <font color=#0000ff>const</font> { Pet::speak(); }
<font color=#0000ff>void</font> eat() <font color=#0000ff>const</font> { Pet::eat(); }
};
<font color=#0000ff>int</font> main() {
Dog simba; <font color=#009900>// Richard's dog</font>
simba.speak();
simba.eat();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The slot in the <B>Pet </B>VTABLE is
still empty, but there happens to be a function by that name that you can call
in the derived class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The other benefit to this feature is that
it allows you to change from an ordinary virtual to a pure virtual without
disturbing the existing code. (This is a way for you to locate classes that
don’t override that virtual
function.)</FONT><A NAME="_Toc305593269"></A><A NAME="_Toc305628741"></A><A NAME="_Toc312374054"></A><A NAME="_Toc472655031"></A><BR></P></DIV>
<A NAME="Heading449"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Inheritance and the
VTABLE<BR><A NAME="Index2491"></A><A NAME="Index2492"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can imagine what happens when you
perform inheritance and override some of the virtual functions. The compiler
creates a new VTABLE for your new class, and it inserts your new function
addresses using the base-class function addresses for any virtual functions you
don’t override. One way or another, for every object that can be created
(that is, its class has no pure virtuals) there’s always a full set of
function addresses in the VTABLE, so you’ll never be able to make a call
to an address that isn’t there (which would be
disastrous).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But what happens when you inherit and add
new virtual functions in the <I>derived</I>
class<A NAME="Index2493"></A><A NAME="Index2494"></A><A NAME="Index2495"></A><A NAME="Index2496"></A>?
Here’s a simple example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C15:AddingVirtuals.cpp</font>
<font color=#009900>// Adding virtuals in derivation</font>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Pet {
string pname;
<font color=#0000ff>public</font>:
Pet(<font color=#0000ff>const</font> string& petName) : pname(petName) {}
<font color=#0000ff>virtual</font> string name() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> pname; }
<font color=#0000ff>virtual</font> string speak() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> <font color=#004488>""</font>; }
};
<font color=#0000ff>class</font> Dog : <font color=#0000ff>public</font> Pet {
string name;
<font color=#0000ff>public</font>:
Dog(<font color=#0000ff>const</font> string& petName) : Pet(petName) {}
<font color=#009900>// New virtual function in the Dog class:</font>
<font color=#0000ff>virtual</font> string sit() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> Pet::name() + <font color=#004488>" sits"</font>;
}
string speak() <font color=#0000ff>const</font> { <font color=#009900>// Override</font>
<font color=#0000ff>return</font> Pet::name() + <font color=#004488>" says 'Bark!'"</font>;
}
};
<font color=#0000ff>int</font> main() {
Pet* p[] = {<font color=#0000ff>new</font> Pet(<font color=#004488>"generic"</font>),<font color=#0000ff>new</font> Dog(<font color=#004488>"bob"</font>)};
cout << <font color=#004488>"p[0]->speak() = "</font>
<< p[0]->speak() << endl;
cout << <font color=#004488>"p[1]->speak() = "</font>
<< p[1]->speak() << endl;
<font color=#009900>//! cout << "p[1]->sit() = "</font>
<font color=#009900>//! << p[1]->sit() << endl; // Illegal</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>Pet</B> contains a two
virtual functions: <B>speak( )</B> and <B>name( )</B>. <B>Dog</B> adds
a third virtual function called <B>sit( )</B>, as well as overriding the
meaning of <B>speak( )</B>. A diagram will help you visualize what’s
happening. Here are the VTABLEs <A NAME="Index2497"></A>created by the compiler
for <B>Pet</B> and <B>Dog</B>:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIC2Vo19.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that the compiler maps the
location of the <B>speak( )</B> address into exactly the same spot in the
<B>Dog </B>VTABLE as it is in the <B>Pet</B> VTABLE. Similarly, if a class
<B>Pug</B> is inherited from <B>Dog</B>,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -