📄 ei43.htm
字号:
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198A5.GIF" BORDER=0></SPAN>
<P><A NAME="dingp12"></A>has a distressing tendency to evolve into one that looks like <NOBR>this:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B5.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_198B5.GIF" BORDER=0></SPAN>
<P><A NAME="dingp13"></A><A NAME="7885"></A>
Now, it may or may not be true that diamonds are a girl's best friend, but it is certainly true that a diamond-shaped inheritance hierarchy such as this is <I>not</I> very friendly. If you create a hierarchy such as this, you are immediately confronted with the question of whether to make <CODE>A</CODE> a virtual base class, i.e., whether inheritance from A should be virtual. In practice, the answer is almost invariably that it should; only rarely will you want an object of type <CODE>D</CODE> to contain multiple copies of the data members of <CODE>A</CODE>. In recognition of this truth, <CODE>B</CODE> and <CODE>C</CODE> above declare <CODE>A</CODE> as a virtual base <NOBR>class.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="7891"></A>
<P><A NAME="dingp14"></A>
Unfortunately, at the time you define <CODE>B</CODE> and <CODE>C</CODE>, you may not know whether any class will decide to inherit from both of them, and in fact you shouldn't need to know this in order to define them correctly. As a class designer, this puts you in a dreadful quandary. If you do <I>not</I> declare <CODE>A</CODE> as a virtual base of <CODE>B</CODE> and <CODE>C</CODE>, a later designer of <CODE>D</CODE> may need to modify the definitions of <CODE>B</CODE> and <CODE>C</CODE> in order to use them effectively. Frequently, this is unacceptable, often because the definitions of <CODE>A</CODE>, <CODE>B</CODE>, and <CODE>C</CODE> are read-only. This would be the case if <CODE>A</CODE>, <CODE>B</CODE>, and <CODE>C</CODE> were in a library, for example, and <CODE>D</CODE> was written by a library <NOBR>client.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="7894"></A>
<P><A NAME="dingp15"></A>
On the other hand, if you <I>do</I> declare <CODE>A</CODE> as a virtual base of <CODE>B</CODE> and <CODE>C</CODE>, you typically impose an additional cost in both space and time on clients of those classes. That is because virtual base classes are often implemented as <I>pointers</I> to objects, rather than as objects themselves. It goes without saying that the layout of objects in memory is compiler-dependent, but the fact remains that the memory layout for an object of type <CODE>D</CODE> with <CODE>A</CODE> as a nonvirtual base is typically a contiguous series of memory locations, whereas the memory layout for an object of type <CODE>D</CODE> with <CODE>A</CODE> as a virtual base is sometimes a contiguous series of memory lo<A NAME="p199"></A>cations, two of which contain pointers to the memory locations containing the data members of the virtual base <NOBR>class:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<SPAN ID="Image3of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image3of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image3of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image3of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image3of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image3of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_199A5.GIF" BORDER=0></SPAN>
<A NAME="7938"></A>
<P><A NAME="dingp16"></A>
Even compilers that don't use this particular implementation strategy generally impose some kind of space penalty for using virtual <NOBR>inheritance.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="22309"></A>
<P><A NAME="dingp17"></A>
In view of these considerations, it would seem that effective class design in the presence of MI calls for clairvoyance on the part of library designers. Seeing as how run-of-the-mill common sense is an increasingly rare commodity these days, you would be ill-advised to rely too heavily on a language feature that calls for designers to be not only anticipatory of future needs, but downright prophetic (see also <A HREF="../MEC/MI32_FR.HTM#5373" TARGET="_top">M32</A>).<SCRIPT>create_link(17);</SCRIPT>
</P>
<A NAME="7940"></A>
<P><A NAME="dingp18"></A>
Of course, this could also be said of the choice between virtual and nonvirtual functions in a base class, but there is a crucial difference. <A HREF="./EI36_FR.HTM#7007" TARGET="_top">Item 36</A> explains that a virtual function has a well-defined high-level meaning that is distinct from the equally well-defined high-level meaning of a nonvirtual function, so it is possible to choose between the two based on what you want to communicate to writers of subclasses. However, the decision whether a base class should be virtual or nonvirtual lacks a well-defined high-level meaning. Rather, that decision is usually based on the structure of the entire inheritance hierarchy, and as such it cannot be made until the entire hierarchy is known. If you need to know exactly how your class is going to be used before you can define it correctly, it becomes very difficult to design effective <NOBR>classes.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="7945"></A>
<P><A NAME="dingp19"></A>
Once you're past the problem of ambiguity and you've settled the question of whether inheritance from your base class(es) should be virtual, <A NAME="p200"></A>still more complications confront you. Rather than belaboring things, I'll simply mention two other issues you need to keep in <NOBR>mind:<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<UL><A NAME="7947"></A>
<A NAME="dingp20"></A><LI><B>Passing constructor arguments to virtual base classes.</B> Under nonvirtual inheritance, arguments for a base class constructor are specified in the member initialization lists of the classes that are immediately derived from the base class. Because single inheritance hierarchies need only nonvirtual bases, arguments are passed up the inheritance hierarchy in a very natural fashion: the classes at level n of the hierarchy pass arguments to the classes at level n-1. For constructors of a virtual base class, however, arguments are specified in the member initialization lists of the classes that are <I>most derived</I> from the base. As a result, the class initializing a virtual base may be arbitrarily far from it in the inheritance graph, and the class performing the initialization can change as new classes are added to the hierarchy. (A good way to avoid this problem is to eliminate the need to pass constructor arguments to virtual bases. The easiest way to do that is to avoid putting data members in such classes. This is the essence of the Java solution to the problem: virtual base classes in Java (i.e., "Interfaces") are prohibited from containing data.)<SCRIPT>create_link(20);</SCRIPT>
<A NAME="22360"></A>
<A NAME="dingp21"></A><LI><B>Dominance of virtual functions.</B> Just when you thought you had ambiguity all figured out, they change the rules on you. Consider again the diamond-shaped inheritance graph involving classes <CODE>A</CODE>, <CODE>B</CODE>, <CODE>C</CODE>, and <CODE>D</CODE>. Suppose that <CODE>A</CODE> defines a virtual member function <CODE>mf</CODE>, and <CODE>C</CODE> redefines it; <CODE>B</CODE> and <CODE>D</CODE>, however, do not redefine <CODE>mf</CODE>:<SCRIPT>create_link(21);</SCRIPT>
</UL><BR>
<SPAN ID="Image4of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image4of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image4of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image4of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image4of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image4of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_200A5.GIF" BORDER=0></SPAN>
<A NAME="22376"></A>
<P><A NAME="dingp22"></A>From our earlier discussion, you'd expect this to be <NOBR>ambiguous:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="7953"></A>
<UL><PRE>D *pd = new D;
pd->mf(); // A::mf or C::mf?
</PRE></UL><A NAME="7954"></A>
<P><A NAME="dingp23"></A>Which <CODE>mf</CODE> should be called for a <CODE>D</CODE> object, the one directly inherited from <CODE>C</CODE> or the one indirectly inherited (via <CODE>B</CODE>) from <CODE>A</CODE>? The answer is that <I>it depends on how <CODE>B</CODE> and <CODE>C</CODE> inherit from <CODE>A</CODE></I>. In particular, if <CODE>A</CODE> is a nonvirtual base of <CODE>B</CODE> or <CODE>C</CODE>, the call is ambiguous, but if <CODE>A</CODE> is a virtual base of both <CODE>B</CODE> and <CODE>C</CODE>, the redefinition of <CODE>mf</CODE> in <CODE>C</CODE> is said to <I>dominate</I> <A NAME="p201"></A>the original definition in <CODE>A</CODE>, and the call to <CODE>mf</CODE> through <CODE>pd</CODE> will resolve (unambiguously) to <CODE>C::mf</CODE>. If you sit down and work it all out, it emerges that this is the behavior you want, but it's kind of a pain to have to sit down and work it all out before it makes <NOBR>sense.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="7966"></A>
<P><A NAME="dingp24"></A>Perhaps by now you agree that MI can lead to complications. Perhaps you are convinced that no one in their right mind would ever use it. Perhaps you are prepared to propose to the international <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver = "self.status = 'ISO ANSI Standardization Committee'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standardization committee</A> that multiple inheritance be removed from the language, or at least to propose to your manager that programmers at your company be physically barred from using <NOBR>it.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp25"></A><A NAME="22423"></A>Perhaps you are being too <NOBR>hasty.<SCRIPT>create_link(25);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp26"></A><A NAME="7967"></A>Bear in mind that the designer of C++ didn't set out to make multiple inheritance hard to use, it just turned out that making all the pieces work together in a more or less reasonable fashion inherently entailed the introduction of certain complexities. In the above discussion, you may have noticed that the bulk of these complexities arise in conjunction with the use of virtual base classes. If you can avoid the use of virtual bases — that is, if you can avoid the creation of the deadly diamond inheritance graph — things become much more <NOBR>manageable.<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp27"></A><A NAME="7973"></A>For example, <A HREF="./EI34_FR.HTM#6793" TARGET="_top">Item 34</A> describes how a <I>Protocol class</I> exists only to specify an interface for derived classes; it has no data members, no constructors, a virtual destructor (see <A HREF="./EI14_FR.HTM#223029" TARGET="_top">Item 14</A>), and a set of pure virtual functions that specify the interface. A Protocol <CODE>Person</CODE> class might look like <NOBR>this:<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>
<A NAME="7980"></A>
<UL><PRE>class Person {
public:
virtual ~Person();
</PRE>
</UL><A NAME="7981"></A>
<UL><PRE> virtual string name() const = 0;
virtual string birthDate() const = 0;
virtual string address() const = 0;
virtual string nationality() const = 0;
};
</PRE>
</UL><A NAME="7982"></A>
<P><A NAME="dingp28"></A>Clients of this class must program in terms of <CODE>Person</CODE> pointers and references, because abstract classes cannot be <NOBR>instantiated.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp29"></A><A NAME="7984"></A>To create objects that can be manipulated as <CODE>Person</CODE> objects, clients of <CODE>Person</CODE> use <I>factory functions</I> (see <A HREF="./EI34_FR.HTM#6793" TARGET="_top">Item 34</A>) to instantiate concrete subclasses of that <NOBR>class:<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>
<A NAME="22430"></A>
<UL><PRE>// factory function to create a Person object from a
// unique database ID
Person * makePerson(DatabaseID personIdentifier);
</PRE>
</UL><A NAME="22643"></A>
<UL><PRE><A NAME="p202"></A>DatabaseID askUserForDatabaseID();
</PRE>
</UL><A NAME="22507"></A>
<UL><PRE>DatabaseID pid = askUserForDatabaseID();
</PRE>
</UL><A NAME="22439"></A>
<UL><PRE>
Person *pp = makePerson(pid); // create object supporting
// the Person interface
</PRE>
</UL><A NAME="22440"></A>
<UL><PRE>
... // manipulate *pp via
// Person's member functions
</PRE>
</UL><A NAME="22443"></A>
<UL><PRE>
delete pp; // delete the object when
// it's no longer needed
</PRE>
</UL><A NAME="22428"></A>
<P><A NAME="dingp30"></A>
This just begs the question: how does <CODE>makePerson</CODE> create the objects to which it returns pointers? Clearly, there must be some concrete class derived from <CODE>Person</CODE> that <CODE>makePerson</CODE> can <NOBR>instantiate.<SCRIPT>create_link(30);</SCRIPT>
</NOBR></P>
<A NAME="22497"></A>
<P><A NAME="dingp31"></A>
Suppose this class is called <CODE>MyPerson</CODE>. As a concrete class, <CODE>MyPerson</CODE> must provide implementations for the pure virtual functions it inherits from <CODE>Person</CODE>. It could write these from scratch, but it would be better software engineering to take advantage of existing components that already do most or all of what's necessary. For example, let's suppose a creaky old database-specific class <CODE>PersonInfo</CODE> already exists that provides the essence of what <CODE>MyPerson</CODE> <NOBR>needs:<SCRIPT>create_link(31);</SCRIPT>
</NOBR></P>
<A NAME="22520"></A>
<UL><PRE>class PersonInfo {
public:
PersonInfo(DatabaseID pid);
virtual ~PersonInfo();
</PRE>
</UL><A NAME="22529"></A>
<UL><PRE> virtual const char * theName() const;
virtual const char * theBirthDate() const;
virtual const char * theAddress() const;
virtual const char * theNationality() const;
</PRE>
</UL><A NAME="22646"></A>
<UL><PRE> virtual const char * valueDelimOpen() const; // see
virtual const char * valueDelimClose() const; // below
</PRE>
</UL><A NAME="22651"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="22652"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="22653"></A>
<P><A NAME="dingp32"></A>
You can tell this is an old class, because the member functions return <CODE>const</CODE> <CODE>char*</CODE>s instead of <CODE>string</CODE> objects. Still, if the shoe fits, why not wear it? The names of this class's member functions suggest that the result is likely to be pretty <NOBR>comfortable.<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>
<A NAME="23111"></A>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -