📄 mi33.htm
字号:
}</PRE>
</UL>
<A NAME="11932"></A>
<P><A NAME="dingp15"></A>
This function assigns <CODE>rhs</CODE> to <CODE>*this</CODE> only if <CODE>rhs</CODE> is really a <CODE>Lizard</CODE>. If it's not, the function propagates the <CODE>bad_cast</CODE> exception that <CODE>dynamic_cast</CODE> throws when the cast fails. (Actually, the type of the exception is <CODE>std</CODE>::<CODE>bad_cast</CODE>, because the components of the standard library, including the exceptions thrown by the standard components, are in the namespace <CODE>std</CODE>. For an overview of the standard library,
see <a href="../EC/EI49_FR.HTM#8392" TARGET="_top">Item E49</A> and <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A>.)<SCRIPT>create_link(15);</SCRIPT>
</P><A NAME="11941"></A>
<P><A NAME="dingp16"></A>Even without worrying about exceptions, this function seems needlessly complicated and expensive — the <CODE>dynamic_cast</CODE> must consult a <CODE>type_info</CODE> structure; see <a href="./MI24_FR.HTM#41284" TARGET="_top">Item 24</A> — in the common case where one <CODE>Lizard</CODE> object is assigned to <NOBR>another:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="32737"></A>
<UL><PRE>Lizard liz1, liz2;
<A NAME="32741"></A>
...
<A NAME="32742"></A>
liz1 = liz2; // no need to perform a
// dynamic_cast: this
// assignment must be valid</PRE>
</UL>
<A NAME="32743"></A>
<P><A NAME="dingp17"></A>
We can handle this case without paying for the complexity or cost of a <CODE>dynamic_cast</CODE> by adding to <CODE>Lizard</CODE> the conventional assignment <NOBR>operator:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="32755"></A>
<UL><PRE><A NAME="p262"></A>class Lizard: public Animal {
public:
virtual Lizard& operator=(const Animal& rhs);
<A NAME="33126"></A>
Lizard& operator=(const Lizard& rhs); // add this
<A NAME="32762"></A>
...
<A NAME="32763"></A>
};
<A NAME="32765"></A>
Lizard liz1, liz2;
<A NAME="76573"></A>
...
<A NAME="76574"></A>
liz1 = liz2; // calls operator= taking
// a const Lizard&
<A NAME="32783"></A>
Animal *pAnimal1 = &liz1;
Animal *pAnimal2 = &liz2;
<A NAME="76575"></A>
...
<A NAME="76576"></A>
*pAnimal1 = *pAnimal2; // calls operator= taking
// a const Animal&</PRE>
</UL>
<A NAME="32798"></A>
<P><A NAME="dingp18"></A>
In fact, given this latter <CODE>operator=</CODE>, it's simplicity itself to implement the former one in terms of <NOBR>it:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="32801"></A>
<UL><PRE>Lizard& Lizard::operator=(const Animal& rhs)
{
return operator=(dynamic_cast<const Lizard&>(rhs));
}</PRE>
</UL>
<A NAME="7564"></A>
<P><A NAME="dingp19"></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(19);</SCRIPT>
</NOBR></P><A NAME="32821"></A>
<P><A NAME="dingp20"></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(20);</SCRIPT>
</NOBR></P><A NAME="32585"></A>
<P><A NAME="dingp21"></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(21);</SCRIPT>
</NOBR></P><A NAME="32858"></A>
<A NAME="p263"></A><P><A NAME="dingp22"></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(22);</SCRIPT>
</NOBR></P>
<A NAME="32867"></A>
<UL><PRE>class Animal {
private:
Animal& operator=(const Animal& rhs); // this is now
... // private
<A NAME="76577"></A>
};
<A NAME="32868"></A>
class Lizard: public Animal {
public:
Lizard& operator=(const Lizard& rhs);
...
<A NAME="76578"></A>
};
<A NAME="32869"></A>
class Chicken: public Animal {
public:
Chicken& operator=(const Chicken& 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 = &liz1;
Animal *pAnimal2 = &chick1;
...
*pAnimal1 = *pAnimal2; // error! attempt to call
// private Animal::operator=</PRE>
</UL>
<A NAME="32905"></A>
<P><A NAME="dingp23"></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(23);</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="dingp24"></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/EI16_FR.HTM#2225" TARGET="_top">Item E16</A>):<SCRIPT>create_link(24);</SCRIPT>
</P>
<A NAME="32910"></A>
<UL><PRE><A NAME="p264"></A>Lizard& Lizard::operator=(const Lizard& rhs)
{
if (this == &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="dingp25"></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(25);</SCRIPT>
</NOBR></P><A NAME="32939"></A>
<P><A NAME="dingp26"></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 —
<CODE>AbstractAnimal</CODE>, say — 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(26);</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>
<A NAME="33037"></A>
<P><A NAME="dingp27"></A>and the class definitions are as <NOBR>follows:<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>
<A NAME="33046"></A>
<UL><PRE>class AbstractAnimal {
protected:
AbstractAnimal& operator=(const AbstractAnimal& 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 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -