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

📄 mc6.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<A NAME="7564"></A>
<P><A NAME="dingp65"></A>
This function attempts to cast <CODE>rhs</CODE> to be a <CODE>Lizard</CODE>. If the cast succeeds, the normal class assignment operator is called. Otherwise, a <CODE>bad_cast</CODE> exception is <NOBR>thrown.<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P><A NAME="32821"></A>
<P><A NAME="dingp66"></A>
Frankly, all this business of checking types at runtime and using <CODE>dynamic_cast</CODE>s makes me nervous. For one thing, some compilers still lack support for <CODE>dynamic_cast</CODE>, so code that uses it, though theoretically portable, is not necessarily portable in practice. More importantly, it requires that clients of <CODE>Lizard</CODE> and <CODE>Chicken</CODE> be prepared to catch <CODE>bad_cast</CODE> exceptions and do something sensible with them each time they perform an assignment. In my experience, there just aren't that many programmers who are willing to program that way. If they don't, it's not clear we've gained a whole lot over our original situation where we were trying to guard against partial <NOBR>assignments.<SCRIPT>create_link(66);</SCRIPT>
</NOBR></P><A NAME="32585"></A>
<P><A NAME="dingp67"></A>
Given this rather unsatisfactory state of affairs regarding virtual assignment operators, it makes sense to regroup and try to find a way to prevent clients from making problematic assignments in the first place. If such assignments are rejected during compilation, we don't have to worry about them doing the wrong <NOBR>thing.<SCRIPT>create_link(67);</SCRIPT>
</NOBR></P><A NAME="32858"></A>
<A NAME="p263"></A><P><A NAME="dingp68"></A>
The easiest way to prevent such assignments is to make <CODE>operator=</CODE> private in <CODE>Animal</CODE>. That way, lizards can be assigned to lizards and chickens can be assigned to chickens, but partial and mixed-type assignments are <NOBR>forbidden:<SCRIPT>create_link(68);</SCRIPT>
</NOBR></P>
<A NAME="32867"></A>
<UL><PRE>class Animal {
private:
  Animal&amp; operator=(const Animal&amp; rhs);               // this is now
  ...                                                 // private
<A NAME="76577"></A>
};
<A NAME="32868"></A>
class Lizard: public Animal {
public:
  Lizard&amp; operator=(const Lizard&amp; rhs);
  ...
<A NAME="76578"></A>
};
<A NAME="32869"></A>
class Chicken: public Animal {
public:
  Chicken&amp; operator=(const Chicken&amp; rhs);
  ...
<A NAME="76579"></A>
};
<A NAME="32876"></A>
Lizard liz1, liz2;
...
liz1 = liz2;                                    // fine
<A NAME="32895"></A>
Chicken chick1, chick2;
...
chick1 = chick2;                                // also fine
<A NAME="32877"></A>
Animal *pAnimal1 = &amp;liz1;
Animal *pAnimal2 = &amp;chick1;
...
*pAnimal1 = *pAnimal2;                          // error! attempt to call
                                                // private Animal::operator=</PRE>
</UL>
<A NAME="32905"></A>
<P><A NAME="dingp69"></A>
Unfortunately, <CODE>Animal</CODE> is a concrete class, and this approach also makes assignments between <CODE>Animal</CODE> objects <NOBR>illegal:<SCRIPT>create_link(69);</SCRIPT>
</NOBR></P>
<A NAME="32906"></A>
<UL><PRE>Animal animal1, animal2;
<A NAME="32907"></A>
...
<A NAME="32908"></A>
animal1 = animal2;                              // error! attempt to call
                                                // private Animal::operator=
</PRE>
</UL>
<A NAME="32909"></A>
<P><A NAME="dingp70"></A>
Moreover, it makes it impossible to implement the <CODE>Lizard</CODE> and <CODE>Chicken</CODE> assignment operators correctly, because assignment operators in derived classes are responsible for calling assignment operators in their base classes (see <A HREF="../EC/EC3_FR.HTM#2225" TARGET="_top">Item E16</A>):<SCRIPT>create_link(70);</SCRIPT>
</P>
<A NAME="32910"></A>
<UL><PRE><A NAME="p264"></A>Lizard&amp; Lizard::operator=(const Lizard&amp; rhs)
{
  if (this == &amp;rhs) return *this;
<A NAME="32919"></A>
  Animal::operator=(rhs);                       // error! attempt to call
                                                // private function. But
                                                // Lizard::operator= must
                                                // call this function to
  ...                                           // assign the Animal parts
}                                               // of *this!
</PRE>
</UL>
<A NAME="32929"></A>
<P><A NAME="dingp71"></A>
We can solve this latter problem by declaring <CODE>Animal</CODE>::<CODE>operator=</CODE> <CODE>protected</CODE>, but the conundrum of allowing assignments between <CODE>Animal</CODE> objects while preventing partial assignments of <CODE>Lizard</CODE> and <CODE>Chicken</CODE> objects through <CODE>Animal</CODE> pointers remains. What's a poor programmer to <NOBR>do?<SCRIPT>create_link(71);</SCRIPT>
</NOBR></P><A NAME="32939"></A>
<P><A NAME="dingp72"></A>
The easiest thing is to eliminate the need to allow assignments between <CODE>Animal</CODE> objects, and the easiest way to do that is to make <CODE>Animal</CODE> an abstract class. As an abstract class, <CODE>Animal</CODE> can't be instantiated, so there will be no need to allow assignments between <CODE>Animal</CODE>s. Of course, this leads to a new problem, because our original design for this system presupposed that <CODE>Animal</CODE> objects were necessary. There is an easy way around this difficulty. Instead of making <CODE>Animal</CODE> itself abstract, we create a new class &#151; <CODE>AbstractAnimal</CODE>, say &#151; consisting of the common features of <CODE>Animal</CODE>, <CODE>Lizard</CODE>, and <CODE>Chicken</CODE> objects, and we make <I>that</I> class abstract. Then we have each of our concrete classes inherit from <CODE>AbstractAnimal</CODE>. The revised hierarchy looks like <NOBR>this,<SCRIPT>create_link(72);</SCRIPT>
</NOBR></P>

<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A5.GIF" BORDER=0></SPAN>

<SPAN ID="Image2of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_264A5.GIF" BORDER=0></SPAN>
<BR>
<A NAME="33037"></A>
<P><A NAME="dingp73"></A>and the class definitions are as <NOBR>follows:<SCRIPT>create_link(73);</SCRIPT>
</NOBR></P>
<A NAME="33046"></A>
<UL><PRE>class AbstractAnimal {
protected:
  AbstractAnimal&amp; operator=(const AbstractAnimal&amp; rhs);
<A NAME="8232"></A>
public:
  virtual ~AbstractAnimal() = 0;                     // see below
  ...
<A NAME="76594"></A>
};
<A NAME="33040"></A>
<A NAME="p265"></A>class Animal: public AbstractAnimal {
public:
  Animal&amp; operator=(const Animal&amp; rhs);
  ...
};
<A NAME="33041"></A>
class Lizard: public AbstractAnimal {
public:
  Lizard&amp; operator=(const Lizard&amp; rhs);
  ...
};
<A NAME="33042"></A>
class Chicken: public AbstractAnimal {
public:
  Chicken&amp; operator=(const Chicken&amp; rhs);
  ...
};</PRE>
</UL>
<A NAME="33038"></A>
<P><A NAME="dingp74"></A>
This design gives you everything you need. Homogeneous assignments are allowed for lizards, chickens, and animals; partial assignments and heterogeneous assignments are prohibited; and derived class assignment operators may call the assignment operator in the base class. Furthermore, none of the code written in terms of the <CODE>Animal</CODE>, <CODE>Lizard</CODE>, or <CODE>Chicken</CODE> classes requires modification, because these classes continue to exist and to behave as they did before <CODE>AbstractAnimal</CODE> was introduced. Sure, such code has to be recompiled, but that's a small price to pay for the security of knowing that assignments that compile will behave intuitively and assignments that would behave unintuitively won't <NOBR>compile.<SCRIPT>create_link(74);</SCRIPT>
</NOBR></P>
<A NAME="33683"></A>
<P><A NAME="dingp75"></A>
For all this to work, <CODE>AbstractAnimal</CODE> must be abstract &#151; it must contain at least one pure virtual function. In most cases, coming up with a suitable function is not a problem, but on rare occasions you may find yourself facing the need to create a class like <CODE>AbstractAnimal</CODE> in which none of the member functions would naturally be declared pure virtual. In such cases, the conventional technique is to make the destructor a pure virtual function; that's what's shown above. In order to support polymorphism through pointers correctly, base classes need virtual destructors anyway (see <A HREF="../EC/EC3_FR.HTM#223029" TARGET="_top">Item E14</A>), so the only cost associated with making such destructors pure virtual is the inconvenience of having to implement them outside their class definitions. (For an example, see <A HREF="./MC5_FR.HTM#17038" TARGET="_top">page 195</A>.)<SCRIPT>create_link(75);</SCRIPT>
</P><A NAME="80971"></A>
<P><A NAME="dingp76"></A>
(If the notion of implementing a pure virtual function strikes you as odd, you just haven't been getting out enough. Declaring a function pure virtual doesn't mean it has no implementation, it <NOBR>means<SCRIPT>create_link(76);</SCRIPT>
</NOBR></P>
<UL><A NAME="80989"></A>
<A NAME="dingp77"></A><LI>the current class is abstract, and<SCRIPT>create_link(77);</SCRIPT>

<A NAME="80990"></A>
<A NAME="dingp78"></A><LI>any concrete class inheriting from the current class must declare the function as a "normal" virtual function (i.e., without the "<CODE>=0</CODE>").<SCRIPT>create_link(78);</SCRIPT>

</UL>
<A NAME="80997"></A>

⌨️ 快捷键说明

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