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

📄 ei36.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 36: Differentiate between inheritance of interface and inheritance of implementation</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI36_DIR.HTM";
var dingtext = "Item E36, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>

<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E36: Interface vs. implementation inheritance" -->
<A NAME="7007"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI35_FR.HTM" TARGET="_top">Item 35: Make sure public inheritance models "isa."</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI37_FR.HTM" TARGET="_top">Item 37: Never redefine an inherited nonvirtual function.</A></FONT></DIV>

<P><A NAME="dingp1"></A><A NAME="p161"></A><FONT ID="eititle">Item 36: &nbsp;Differentiate between inheritance of interface and inheritance of implementation.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="7008"></A>
<P><A NAME="dingp2"></A>
The seemingly straightforward notion of (public) inheritance turns out, upon closer examination, to be composed of two separable parts: inheritance of function interfaces and inheritance of function implementations. The difference between these two kinds of inheritance corresponds exactly to the difference between function declarations and function definitions discussed in the <A HREF="./EIINTRFR.HTM" TARGET="_top">Introduction</a> to this <NOBR>book.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="7009"></A>
<P><A NAME="dingp3"></A>
As a class designer, you sometimes want derived classes to inherit only the interface (declaration) of a member function; sometimes you want derived classes to inherit both the interface and the implementation for a function, but you want to allow them to override the implementation you provide; and sometimes you want them to inherit both interface and implementation without allowing them to override <NOBR>anything.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="7010"></A>
<P><A NAME="dingp4"></A>
To get a better feel for the differences among these options, consider a class hierarchy for representing geometric shapes in a graphics <NOBR>application:<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="7012"></A>
<UL><PRE>class Shape {
public:
  virtual void draw() const = 0;
</PRE>
</UL><A NAME="7013"></A>
<UL><PRE>  virtual void error(const string&amp; msg);
</PRE>
</UL><A NAME="7014"></A>
<UL><PRE>  int objectID() const;
</PRE>
</UL><A NAME="7015"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="7016"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7018"></A>
<UL><PRE>class Rectangle: public Shape { ... };
</PRE>
</UL><A NAME="7020"></A>
<UL><PRE>class Ellipse: public Shape { ... };
</PRE>
</UL><A NAME="7022"></A>
<P><A NAME="dingp5"></A>
<CODE>Shape</CODE> is an abstract class; its pure virtual function <CODE>draw</CODE> marks it as such. As a result, clients cannot create instances of the <CODE>Shape</CODE> class, only of the classes derived from it. Nonetheless, <CODE>Shape</CODE> exerts a strong influence on all classes that (publicly) inherit from it, <NOBR>because<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P><UL><A NAME="7023"></A>
<P><A NAME="dingp6"></A><LI>Member function <I>interfaces are always inherited</I>. As explained in <A HREF="./EI35_FR.HTM#6914" TARGET="_top">Item 35</A>, public inheritance means isa, so anything that is true of a base class must also be true of its derived classes. Hence, if a function applies to a class, it must also apply to its subclasses.<SCRIPT>create_link(6);</SCRIPT>

</UL></P>
<A NAME="7027"></A>
<P><A NAME="dingp7"></A>
Three functions are declared in the <CODE>Shape</CODE> class. The first, <CODE>draw</CODE>, draws the current object on an implicit display. The second, <CODE>error</CODE>, is called by member functions if they need to report an error. The third, <CODE>objectID</CODE>, returns a unique integer identifier for the current object; <A HREF="./EI17_FR.HTM#2264" TARGET="_top">Item 17</A> <A NAME="p162"></A>gives an example of how such a function might be used. Each function is declared in a different way: <CODE>draw</CODE> is a pure virtual function; <CODE>error</CODE> is a simple (impure?) virtual function; and <CODE>objectID</CODE> is a nonvirtual function. What are the implications of these different <NOBR>declarations?<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="7031"></A>
<P><A NAME="dingp8"></A>
Consider first the pure virtual function <CODE>draw</CODE>. The two most salient features of pure virtual functions are that they <I>must</I> be redeclared by any concrete class that inherits them, and they typically have no definition in abstract classes. Put these two traits together, and you realize <NOBR>that<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="7033"></A>
<P>
<UL><A NAME="dingp9"></A><LI>The purpose of declaring a pure virtual function is to have derived classes inherit a function <I>interface only</I>.<SCRIPT>create_link(9);</SCRIPT>

</UL></P>
<A NAME="7034"></A>
<P><A NAME="dingp10"></A>
This makes perfect sense for the <CODE>Shape::draw</CODE> function, because it is a reasonable demand that all <CODE>Shape</CODE> objects must be <CODE>draw</CODE>able, but the <CODE>Shape</CODE> class can provide no reasonable default implementation for that function. The algorithm for drawing an ellipse is very different from the algorithm for drawing a rectangle, for example. A good way to interpret the declaration of <CODE>Shape::draw</CODE> is as saying to designers of subclasses, "You must provide a <CODE>draw</CODE> function, but I have no idea how you're going to implement <NOBR>it."<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="7036"></A>
<P><A NAME="dingp11"></A>
Incidentally, it <I>is</I> possible to provide a definition for a pure virtual function. That is, you could provide an implementation for <CODE>Shape::draw</CODE>, and C++ wouldn't complain, but the only way to call it would be to fully specify the call with the class <NOBR>name:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="7037"></A>
<UL><PRE>
Shape *ps = new Shape;           // error! Shape is abstract
</PRE>
</UL><A NAME="7038"></A>
<UL><PRE>
Shape *ps1 = new Rectangle;      // fine
ps1-&gt;draw();                     // calls Rectangle::draw
</PRE>
</UL><A NAME="7039"></A>
<UL><PRE>
Shape *ps2 = new Ellipse;        // fine
ps2-&gt;draw();                     // calls Ellipse::draw
</PRE>
</UL><A NAME="7040"></A>
<UL><PRE>
ps1-&gt;Shape::draw();              // calls Shape::draw
</PRE>
</UL><A NAME="7041"></A>
<UL><PRE>
ps2-&gt;Shape::draw();              // calls Shape::draw
</PRE>
</UL></P>
<A NAME="7042"></A>
<P><A NAME="dingp12"></A>
Aside from helping impress fellow programmers at cocktail parties, knowledge of this feature is generally of limited utility. As you'll see below, however, it can be employed as a mechanism for providing a safer-than-usual default implementation for simple (impure) virtual <NOBR>functions.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="7043"></A>
<P><A NAME="dingp13"></A>
Sometimes it's useful to declare a class containing <I>nothing</I> but pure virtual functions. Such a <I>Protocol class</I> can provide only function interfaces for derived classes, never implementations. Protocol classes are described in <A HREF="./EI34_FR.HTM#6793" TARGET="_top">Item 34</A> and are mentioned again in <A HREF="./EI43_FR.HTM#7778" TARGET="_top">Item 43</A>.<SCRIPT>create_link(13);</SCRIPT>
</P>
<A NAME="7053"></A>
<P><A NAME="dingp14"></A>
<A NAME="p163"></A>The story behind simple virtual functions is a bit different from that behind pure virtuals. As usual, derived classes inherit the interface of the function, but simple virtual functions traditionally provide an implementation that derived classes may or may not choose to override. If you think about this for a minute, you'll realize <NOBR>that<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P><UL><A NAME="7054"></A>
<P>
<A NAME="dingp15"></A><LI>The purpose of declaring a simple virtual function is to have derived classes inherit a function <I>interface as well as a default implementation</I>.<SCRIPT>create_link(15);</SCRIPT>

</UL></P>
<A NAME="7055"></A>
<P><A NAME="dingp16"></A>
In the case of <CODE>Shape::error</CODE>, the interface says that every class must support a function to be called when an error is encountered, but each class is free to handle errors in whatever way it sees fit. If a class doesn't want to do anything special, it can just fall back on the default error-handling provided in the <CODE>Shape</CODE> class. That is, the declaration of <CODE>Shape::error</CODE> says to designers of subclasses, "You've got to support an <CODE>error</CODE> function, but if you don't want to write your own, you can fall back on the default version in the <CODE>Shape</CODE> <NOBR>class."<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="7056"></A>
<P><A NAME="dingp17"></A>
It turns out that it can be dangerous to allow simple virtual functions to specify both a function declaration and a default implementation. To see why, consider a hierarchy of airplanes for XYZ Airlines. XYZ has only two kinds of planes, the Model A and the Model B, and both are flown in exactly the same way. Hence, XYZ designs the following <NOBR>hierarchy:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="7059"></A>
<UL><PRE>class Airport { ... };      // represents airports
</PRE>
</UL><A NAME="7061"></A>
<UL><PRE>class Airplane {
public:
  virtual void fly(const Airport&amp; destination);
</PRE>
</UL><A NAME="7062"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="7063"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="7065"></A>
<UL><PRE>void Airplane::fly(const Airport&amp; destination)
{
  <I>default code for flying an airplane to
  the given destination</I>
}
</PRE>
</UL><A NAME="7067"></A>
<UL><PRE>class ModelA: public Airplane { ... };
</PRE>
</UL><A NAME="7069"></A>
<UL><PRE>class ModelB: public Airplane { ... };
</PRE>
</UL><A NAME="7070"></A>
<P><A NAME="dingp18"></A>
To express that all planes have to support a <CODE>fly</CODE> function, and in recognition of the fact that different models of plane could, in principle, require different implementations for <CODE>fly</CODE>, <CODE>Airplane::fly</CODE> is declared virtual. However, in order to avoid writing identical code in the <CODE>ModelA</CODE> <A NAME="p164"></A>and <CODE>ModelB</CODE> classes, the default flying behavior is provided as the body of <CODE>Airplane::fly</CODE>, which both <CODE>ModelA</CODE> and <CODE>ModelB</CODE> <NOBR>inherit.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="19713"></A>
<P><A NAME="dingp19"></A>
This is a classic object-oriented design. Two classes share a common feature (the way they implement <CODE>fly</CODE>), so the common feature is moved into a base class, and the feature is inherited by the two classes. This design makes common features explicit, avoids code duplication, facilitates future enhancements, and eases long-term maintenance &#151; all the things for which object-oriented technology is so highly touted. XYZ Airlines should be <NOBR>proud.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="7071"></A>
<P><A NAME="dingp20"></A>
Now suppose that XYZ, its fortunes on the rise, decides to acquire a new type of airplane, the Model C. The Model C differs from the Model A and the Model B. In particular, it is flown <NOBR>differently.<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="7072"></A>
<P><A NAME="dingp21"></A>
XYZ's programmers add the class for Model C to the hierarchy, but in their haste to get the new model into service, they forget to redefine the <CODE>fly</CODE> <NOBR>function:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="7074"></A>
<UL><PRE>class ModelC: public Airplane {
</PRE>
</UL><A NAME="7075"></A>
<UL><PRE>
  ...                          // no fly function is
                               // declared
};
</PRE>
</UL><A NAME="7077"></A>
<A NAME="dingp22"></A>In their code, then, they have something akin to the <NOBR>following:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="7078"></A>
<UL><PRE>
Airport JFK(...);              // JFK is an airport in
                               // New York City
</PRE>
</UL><A NAME="7079"></A>
<UL><PRE>Airplane *pa = new ModelC;
</PRE>
</UL><A NAME="7080"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="7081"></A>

⌨️ 快捷键说明

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