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

📄 mi33.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 33: Make non-leaf classes abstract</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

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

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="M33: Make non-leaf classes abstract" -->
<A NAME="10947"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI32_FR.HTM" TARGET="_top">Item 32: Program in the future tense</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI34_FR.HTM" TARGET="_top">Item 34: Understand how to combine C++ and C in the same program</A></FONT></DIV>

<A NAME="p258"></A>

<P><A NAME="dingp1"></A><font ID="mititle">Item 33: &nbsp;Make non-leaf classes abstract.</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="72192"></A><A NAME="32371"></A>
<P><A NAME="dingp2"></A>
Suppose you're working on a project whose software deals with animals. Within this software, most animals can be treated pretty much the same, but two kinds of animals &#151; lizards and chickens &#151; require special handling. That being the case, the obvious way to relate the classes for animals, lizards, and chickens is like <NOBR>this:<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>

<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A5.GIF" BORDER=0></SPAN>

<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_258A5.GIF" BORDER=0></SPAN>

<A NAME="32440"></A>
<P><A NAME="dingp3"></A>
The <CODE>Animal</CODE> class embodies the features shared by all the creatures you deal with, and the <CODE>Lizard</CODE> and <CODE>Chicken</CODE> classes specialize <CODE>Animal</CODE> in ways appropriate for lizards and chickens, <NOBR>respectively.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P><A NAME="32462"></A>
<P><A NAME="dingp4"></A>
Here's a sketch of the definitions for these <NOBR>classes:<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="32466"></A>
<UL><PRE>class Animal {
public:
  Animal&amp; operator=(const Animal&amp; rhs);
  ...
<A NAME="76532"></A>
};
<A NAME="32469"></A>
<A NAME="p259"></A>class Lizard: public Animal {
public:
  Lizard&amp; operator=(const Lizard&amp; rhs);
  ...
<A NAME="76533"></A>
};
<A NAME="32472"></A>
class Chicken: public Animal {
public:
  Chicken&amp; operator=(const Chicken&amp; rhs);
  ...
<A NAME="76534"></A>
};</PRE>
</UL>
<A NAME="32464"></A>
<P><A NAME="dingp5"></A>
Only the assignment operators are shown here, but that's more than enough to keep us busy for a while. Consider this <NOBR>code:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="32112"></A>
<UL><PRE>Lizard liz1;
Lizard liz2;
</PRE>
</UL><A NAME="32640"></A>
<UL><PRE>Animal *pAnimal1 = &amp;liz1;
Animal *pAnimal2 = &amp;liz2;
</PRE>
</UL><A NAME="32114"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="32115"></A>
<UL><PRE>*pAnimal1 = *pAnimal2;
</PRE>
</UL><A NAME="32116"></A>
<P><A NAME="dingp6"></A>
There are two problems here. First, the assignment operator invoked on the last line is that of the <CODE>Animal</CODE> class, even though the objects involved are of type <CODE>Lizard</CODE>. As a result, only the <CODE>Animal</CODE> part of <CODE>liz1</CODE> will be modified. This is a <I>partial</I> assignment. After the assignment, <CODE>liz1</CODE>'s <CODE>Animal</CODE> members have the values they got from <CODE>liz2</CODE>, but <CODE>liz1</CODE>'s <CODE>Lizard</CODE> members remain <NOBR>unchanged.<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P><A NAME="32117"></A>
<P><A NAME="dingp7"></A>
The second problem is that real programmers write code like this. It's not uncommon to make assignments to objects through pointers, especially for experienced C programmers who have moved to C++. That being the case, we'd like to make the assignment behave in a more reasonable fashion. As <a href="./MI32_FR.HTM#5373" TARGET="_top">Item 32</A> points out, our classes should be easy to use correctly and difficult to use incorrectly, and the classes in the hierarchy above are easy to use <NOBR>incorrectly.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P><A NAME="32557"></A>
<P><A NAME="dingp8"></A>
One approach to the problem is to make the assignment operators virtual. If <CODE>Animal</CODE>::<CODE>operator=</CODE> were virtual, the assignment would invoke the <CODE>Lizard</CODE> assignment operator, which is certainly the correct one to call. However, look what happens if we declare the assignment operators <NOBR>virtual:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="32119"></A>
<UL><PRE>class Animal {
public:
  virtual Animal&amp; operator=(const Animal&amp; rhs);
  ...
};
<A NAME="32120"></A>
<A NAME="p260"></A>class Lizard: public Animal {
public:
  virtual Lizard&amp; operator=(const Animal&amp; rhs);
  ...
};
<A NAME="32121"></A>
class Chicken: public Animal {
public:
  virtual Chicken&amp; operator=(const Animal&amp; rhs);
  ...
};
</PRE>
</UL><A NAME="32122"></A>
<P><A NAME="dingp9"></A>
Due to relatively recent changes to the language, we can customize the return value of the assignment operators so that each returns a reference to the correct class, but the rules of C++ force us to declare identical <I>parameter</I> types for a virtual function in every class in which it is declared. That means the assignment operator for the <CODE>Lizard</CODE> and <CODE>Chicken</CODE> classes must be prepared to accept <I>any</I> kind of <CODE>Animal</CODE> object on the right-hand side of an assignment. That,
in turn, means we have to confront the fact that code like the following is <NOBR>legal:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>

<A NAME="32623"></A>
<UL><PRE>Lizard liz;
Chicken chick;
<A NAME="32624"></A>
Animal *pAnimal1 = &amp;liz;
Animal *pAnimal2 = &amp;chick;
<A NAME="32625"></A>
...
<A NAME="32626"></A>
*pAnimal1 = *pAnimal2;                 // assign a chicken to
                                       // a lizard!</PRE>
</UL>
<A NAME="32621"></A>

<P><A NAME="dingp10"></A>
This is a mixed-type assignment: a <CODE>Lizard</CODE> is on the left and a <CODE>Chicken</CODE> is on the right. Mixed-type assignments aren't usually a problem in C++, because the language's strong typing generally renders them illegal. By making <CODE>Animal</CODE>'s assignment operator virtual, however, we opened the door to such mixed-type <NOBR>operations.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P><A NAME="32668"></A>
<P><A NAME="dingp11"></A>
This puts us in a difficult position. We'd like to allow same-type assignments through pointers, but we'd like to forbid mixed-type assignments through those same pointers. In other words, we want to allow <NOBR>this,<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>

<A NAME="32671"></A>
<UL><PRE>Animal *pAnimal1 = &amp;liz1;
Animal *pAnimal2 = &amp;liz2;
<A NAME="32672"></A>
...
<A NAME="32673"></A>
*pAnimal1 = *pAnimal2;                 // assign a lizard to a lizard</PRE>
</UL>

<A NAME="32669"></A>
<A NAME="p261"></A><A NAME="dingp12"></A>but we want to prohibit this:<SCRIPT>create_link(12);</SCRIPT>

<A NAME="32678"></A>
<UL><PRE>Animal *pAnimal1 = &amp;liz;
Animal *pAnimal2 = &amp;chick;
<A NAME="32679"></A>
...
<A NAME="32680"></A>
*pAnimal1 = *pAnimal2;                 // assign a chicken to a lizard</PRE>
</UL>
<A NAME="32676"></A>

<P><A NAME="dingp13"></A>
Distinctions such as these can be made only at runtime, because sometimes assigning <CODE>*pAnimal2</CODE> to <CODE>*pAnimal1</CODE> is valid, sometimes it's not. We thus enter the murky world of type-based runtime errors. In particular, we need to signal an error inside <CODE>operator=</CODE> if we're faced with a mixed-type assignment, but if the types are the same, we want to perform the assignment in the usual <NOBR>fashion.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P><A NAME="33107"></A>
<P><A NAME="dingp14"></A>
We can use a <CODE>dynamic_cast</CODE> (see <a href="./MI2_FR.HTM#77216" TARGET="_top">Item 2</A>) to implement this behavior. Here's how to do it for <CODE>Lizard</CODE>'s assignment <NOBR>operator:<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>

<A NAME="32592"></A>
<UL><PRE>Lizard&amp; Lizard::operator=(const Animal&amp; rhs)
{
  // make sure rhs is really a lizard
  const Lizard&amp; rhs_liz = dynamic_cast&lt;const Lizard&amp;&gt;(rhs);
<A NAME="32593"></A>
<I>  proceed with a normal assignment of rhs_liz to *this;</I>

⌨️ 快捷键说明

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