📄 ec6.htm
字号:
</UL><A NAME="7174"></A>
<P><A NAME="dingp88"></A>
Even without knowing anything about <CODE>B</CODE>, <CODE>D</CODE>, or <CODE>mf</CODE>, given an object <CODE>x</CODE> of type <CODE>D</CODE>,<SCRIPT>create_link(88);</SCRIPT>
</P>
<A NAME="7175"></A>
<UL><PRE>
D x; // x is an object of type D
</PRE>
</UL><A NAME="7176"></A><p><A NAME="dingp89"></A>you would probably be quite surprised if <NOBR>this,<SCRIPT>create_link(89);</SCRIPT>
</NOBR></P>
<A NAME="7177"></A>
<UL><PRE>
B *pB = &x; // get pointer to x
</PRE>
</UL><A NAME="7178"></A>
<UL><PRE>
pB->mf(); // call mf through pointer
</PRE>
</UL><A NAME="7179"></A><p><A NAME="dingp90"></A>
behaved differently from <NOBR>this:<SCRIPT>create_link(90);</SCRIPT>
</NOBR></p>
<A NAME="7180"></A>
<UL><PRE>
D *pD = &x; // get pointer to x
</PRE>
</UL><A NAME="7181"></A>
<UL><PRE>
pD->mf(); // call mf through pointer
</PRE>
</UL><A NAME="7182"></A>
<P><A NAME="dingp91"></A>
That's because in both cases you're invoking the member function <CODE>mf</CODE> on the object <CODE>x</CODE>. Because it's the same function and the same object in both cases, it should behave the same way, <NOBR>right?<SCRIPT>create_link(91);</SCRIPT>
</NOBR></P>
<A NAME="7183"></A>
<P><A NAME="dingp92"></A>
Right, it should. But it might not. In particular, it won't if <CODE>mf</CODE> is nonvirtual and <CODE>D</CODE> has defined its own version of <CODE>mf</CODE>:<SCRIPT>create_link(92);</SCRIPT>
</P>
<A NAME="7184"></A>
<UL><PRE>class D: public B {
public:
void mf(); // hides B::mf; see <A HREF="./EC7_FR.HTM#8569" TARGET="_top">Item 50</A>
</PRE>
</UL><A NAME="7188"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="7189"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7190"></A>
<UL><PRE><A NAME="p170"></A>
pB->mf(); // calls B::mf
</PRE>
</UL><A NAME="7191"></A>
<UL><PRE>
pD->mf(); // calls D::mf
</PRE>
</UL><A NAME="7192"></A>
<P><A NAME="dingp93"></A>
The reason for this two-faced behavior is that <I>nonvirtual</I> functions like <CODE>B::mf</CODE> and <CODE>D::mf</CODE> are statically bound (see <A HREF="#177948">Item 38</A>). That means that because <CODE>pB</CODE> is declared to be of type pointer-to-<CODE>B</CODE>, nonvirtual functions invoked through <CODE>pB</CODE> will <I>always</I> be those defined for class <CODE>B</CODE>, even if <CODE>pB</CODE> points to an object of a class derived from <CODE>B</CODE>, as it does in this <NOBR>example.<SCRIPT>create_link(93);</SCRIPT>
</NOBR></P>
<A NAME="19867"></A>
<P><A NAME="dingp94"></A>
<I>Virtual</I> functions, on the other hand, are dynamically bound (again, see <A HREF="#177948">Item 38</A>), so they don't suffer from this problem. If <CODE>mf</CODE> were a virtual function, a call to <CODE>mf</CODE> through either <CODE>pB</CODE> or <CODE>pD</CODE> would result in an invocation of <CODE>D::mf</CODE>, because what <CODE>pB</CODE> and <CODE>pD</CODE> <I>really</I> point to is an object of type <CODE>D</CODE>.<SCRIPT>create_link(94);</SCRIPT>
</P>
<A NAME="7202"></A>
<P><A NAME="dingp95"></A>
The bottom line, then, is that if you are writing class <CODE>D</CODE> and you redefine a nonvirtual function <CODE>mf</CODE> that you inherit from class <CODE>B</CODE>, <CODE>D</CODE> objects will likely exhibit schizophrenic behavior. In particular, any given <CODE>D</CODE> object may act like either a <CODE>B</CODE> or a <CODE>D</CODE> when <CODE>mf</CODE> is called, and the determining factor will have nothing to do with the object itself, but with the declared type of the pointer that points to it. References exhibit the same baffling behavior as do <NOBR>pointers.<SCRIPT>create_link(95);</SCRIPT>
</NOBR></P>
<A NAME="7203"></A>
<P><A NAME="dingp96"></A>
So much for the pragmatic argument. What you want now, I know, is some kind of theoretical justification for not redefining inherited nonvirtual functions. I am pleased to <NOBR>oblige.<SCRIPT>create_link(96);</SCRIPT>
</NOBR></P>
<A NAME="7210"></A>
<P><A NAME="dingp97"></A>
<A HREF="#6914">Item 35</A> explains that public inheritance means isa, and <A HREF="#7007">Item 36</A> describes why declaring a nonvirtual function in a class establishes an invariant over specialization for that class. If you apply these observations to the classes <CODE>B</CODE> and <CODE>D</CODE> and to the nonvirtual member function <CODE>B::mf</CODE>, <NOBR>then<SCRIPT>create_link(97);</SCRIPT>
</NOBR></P>
<UL><A NAME="7211"></A>
<A NAME="dingp98"></A><LI>Everything that is applicable to <CODE>B</CODE> objects is also applicable to <CODE>D</CODE> objects, because every <CODE>D</CODE> object isa <CODE>B</CODE> object;<SCRIPT>create_link(98);</SCRIPT>
<A NAME="7212"></A>
<A NAME="dingp99"></A><LI>Subclasses of <CODE>B</CODE> must inherit both the interface <I>and</I> the implementation of <CODE>mf</CODE>, because <CODE>mf</CODE> is nonvirtual in <CODE>B</CODE>.<SCRIPT>create_link(99);</SCRIPT>
</UL></P>
<A NAME="7214"></A>
<P><A NAME="dingp100"></A>
Now, if <CODE>D</CODE> redefines <CODE>mf</CODE>, there is a contradiction in your design. If <CODE>D</CODE> <EM>really</EM> needs to implement <CODE>mf</CODE> differently from <CODE>B</CODE>, and if every <CODE>B</CODE> object — no matter how specialized — <EM>really</EM> has to use the <CODE>B</CODE> implementation for <CODE>mf</CODE>, then it's simply not true that every <CODE>D</CODE> isa <CODE>B</CODE>. In that case, <CODE>D</CODE> shouldn't publicly inherit from <CODE>B</CODE>. On the other hand, if <CODE>D</CODE> <EM>really</EM> has to publicly inherit from <CODE>B</CODE>, and if <CODE>D</CODE> <EM>really</EM> needs to implement <CODE>mf</CODE> differently from <CODE>B</CODE>, then it's just not true that <CODE>mf</CODE> reflects an invariant over specialization for <CODE>B</CODE>. In that case, <CODE>mf</CODE> should be virtual. Finally, if every <CODE>D</CODE> <EM>really</EM> isa <CODE>B</CODE>, <A NAME="p171"></A>and if <CODE>mf</CODE> really corresponds to an invariant over specialization for <CODE>B</CODE>, then <CODE>D</CODE> can't honestly need to redefine <CODE>mf</CODE>, and it shouldn't try to do <NOBR>so.<SCRIPT>create_link(100);</SCRIPT>
</NOBR></P>
<A NAME="177946"></A>
<P><A NAME="dingp101"></A>
Regardless of which argument applies, something has to give, and under no conditions is it the prohibition on redefining an inherited nonvirtual <NOBR>function.<SCRIPT>create_link(101);</SCRIPT>
</NOBR></P>
<!-- SectionName="E38: Never redefine an inherited default param" -->
<A NAME="177948"></A><A NAME="7218"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#7167">Item 37: Never redefine an inherited nonvirtual function.</A>
<BR>Continue to <A HREF="#7269">Item 39: Avoid casts down the inheritance hierarchy.</A></FONT></DIV>
<P><A NAME="dingp102"></A><FONT ID="eititle">Item 38: Never redefine an inherited default parameter value.</FONT><SCRIPT>create_link(102);</SCRIPT>
</P>
<P><A NAME="dingp103"></A>
Let's simplify this discussion right from the start. A default parameter can exist only as part of a function, and you can inherit only two kinds of functions: virtual and nonvirtual. Therefore, the only way to redefine a default parameter value is to redefine an inherited function. However, it's always a mistake to redefine an inherited nonvirtual function (see <A HREF="#7167">Item 37</A>), so we can safely limit our discussion here to the situation in which you inherit a <I>virtual</I> function with a default parameter <NOBR>value.<SCRIPT>create_link(103);</SCRIPT>
</NOBR></P>
<A NAME="7222"></A>
<P><A NAME="dingp104"></A>
That being the case, the justification for this Item becomes quite straightforward: virtual functions are dynamically bound, but default parameter values are statically <NOBR>bound.<SCRIPT>create_link(104);</SCRIPT>
</NOBR></P>
<A NAME="7223"></A>
<P><A NAME="dingp105"></A>
What's that? You say you're not up on the latest object-oriented lingo, or perhaps the difference between static and dynamic binding has slipped your already overburdened mind? Let's review, <NOBR>then.<SCRIPT>create_link(105);</SCRIPT>
</NOBR></P>
<A NAME="7225"></A>
<P><A NAME="dingp106"></A>
An object's <I>static type</I> is the type you declare it to have in the program text. Consider this class <NOBR>hierarchy:<SCRIPT>create_link(106);</SCRIPT>
</NOBR></P>
<A NAME="7226"></A>
<UL><PRE>enum ShapeColor { RED, GREEN, BLUE };
</PRE>
</UL><A NAME="7227"></A>
<UL><PRE>// a class for geometric shapes
class Shape {
public:
// all shapes must offer a function to draw themselves
virtual void draw(ShapeColor color = RED) const = 0;
</PRE>
</UL><A NAME="7229"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="7230"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7232"></A>
<UL><PRE>class Rectangle: public Shape {
public:
// notice the different default parameter value - bad!
virtual void draw(ShapeColor color = GREEN) const;
</PRE>
</UL><A NAME="7233"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="7234"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7236"></A>
<UL><PRE><A NAME="p172"></A>class Circle: public Shape {
public:
virtual void draw(ShapeColor color) const;
</PRE>
</UL><A NAME="7237"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="7238"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7246"></A>
<P><A NAME="dingp107"></A>
Graphically, it looks like this:<SCRIPT>create_link(107);</SCRIPT>
</P>
<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_172A5.GIF" BORDER=0></SPAN>
<A NAME="7247"></A>
<P><A NAME="dingp108"></A>
Now consider these <NOBR>pointers:<SCRIPT>create_link(108);</SCRIPT>
</NOBR></P>
<A NAME="7248"></A>
<UL><PRE>Shape *ps; // static type = Shape*
</PRE>
</UL><A NAME="7249"></A>
<UL><PRE>Shape *pc = new Circle; // static type = Shape*
</PRE>
</UL><A NAME="7250"></A>
<UL><PRE>Shape *pr = new Rectangle; // static type = Shape*
</PRE>
</UL><A NAME="7251"></A>
<P><A NAME="dingp109"></A>
In this example, <CODE>ps</CODE>, <CODE>pc</CODE>, and <CODE>pr</CODE> are all declared to be of type pointer-to-<CODE>Shape</CODE>, so they all have that as their static type. Notice that it makes absolutely no difference what they're <I>really</I> pointing to — their static type is <CODE>Shape*</CODE> <NOBR>regardless.<SCRIPT>create_link(109);</SCRIPT>
</NOBR></P>
<A NAME="7253"></A>
<P><A NAME="dingp110"></A>
An object's <I>dynamic type</I> is determined by the type of the object to which it currently refers. That is, its dynamic type indicates how it will behave. In the example above, <CODE>pc</CODE>'s dynamic type is <CODE>Circle*</CODE>, and <CODE>pr</CODE>'s dynamic type is <CODE>Rectangle*</CODE>. As for <CODE>ps</CODE>, it doesn't really have a dynamic type, because it doesn't refer to any object <NOBR>(yet).<SCRIPT>create_link(110);</SCRIPT>
</NOBR></P>
<A NAME="7254"></A>
<P><A NAME="dingp111"></A>
Dynamic types, as their name suggests, can change as a program runs, typically through <NOBR>assignments:<SCRIPT>create_link(111);</SCRIPT>
</NOBR></P>
<A NAME="7255"></A>
<UL><PRE>
ps = pc; // ps's dynamic type is
// now Circle*
</PRE>
</UL><A NAME="7256"></A>
<UL><PRE>
ps = pr; // ps's dynamic type is
// now Rectangle*
</PRE>
</UL><A NAME="
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -