📄 mc6.htm
字号:
</NOBR></P><A NAME="32557"></A>
<P><A NAME="dingp54"></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(54);</SCRIPT>
</NOBR></P>
<A NAME="32119"></A>
<UL><PRE>class Animal {
public:
virtual Animal& operator=(const Animal& rhs);
...
};
<A NAME="32120"></A>
<A NAME="p260"></A>class Lizard: public Animal {
public:
virtual Lizard& operator=(const Animal& rhs);
...
};
<A NAME="32121"></A>
class Chicken: public Animal {
public:
virtual Chicken& operator=(const Animal& rhs);
...
};
</PRE>
</UL><A NAME="32122"></A>
<P><A NAME="dingp55"></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(55);</SCRIPT>
</NOBR></P>
<A NAME="32623"></A>
<UL><PRE>Lizard liz;
Chicken chick;
<A NAME="32624"></A>
Animal *pAnimal1 = &liz;
Animal *pAnimal2 = &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="dingp56"></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(56);</SCRIPT>
</NOBR></P><A NAME="32668"></A>
<P><A NAME="dingp57"></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(57);</SCRIPT>
</NOBR></P>
<A NAME="32671"></A>
<UL><PRE>Animal *pAnimal1 = &liz1;
Animal *pAnimal2 = &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="dingp58"></A>
<A NAME="p261"></A>but we want to prohibit this:<SCRIPT>create_link(58);</SCRIPT>
<A NAME="32678"></A>
<UL><PRE>Animal *pAnimal1 = &liz;
Animal *pAnimal2 = &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="dingp59"></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(59);</SCRIPT>
</NOBR></P>
<A NAME="33107"></A>
<P><A NAME="dingp60"></A>We can use a <CODE>dynamic_cast</CODE> (see <A HREF="./MC1_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(60);</SCRIPT>
</NOBR></P>
<A NAME="32592"></A>
<UL><PRE>Lizard& Lizard::operator=(const Animal& rhs)
{
// make sure rhs is really a lizard
const Lizard& rhs_liz = dynamic_cast<const Lizard&>(rhs);
<A NAME="32593"></A>
<I> proceed with a normal assignment of rhs_liz to *this;</I>
<A NAME="32710"></A>
}</PRE>
</UL>
<A NAME="11932"></A>
<P><A NAME="dingp61"></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/EC7_FR.HTM#8392" TARGET="_top">Item E49</A> and <A HREF="#5473">Item 35</A>.)<SCRIPT>create_link(61);</SCRIPT>
</P><A NAME="11941"></A>
<P><A NAME="dingp62"></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="./MC4_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(62);</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="dingp63"></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(63);</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="dingp64"></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(64);</SCRIPT>
</NOBR></P>
<A NAME="32801"></A>
<UL><PRE>Lizard& Lizard::operator=(const Animal& rhs)
{
return operator=(dynamic_cast<const Lizard&>(rhs));
}</PRE>
</UL>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -