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

📄 mc6.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
</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&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="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 = &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="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 = &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="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 = &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="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&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>
<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 &#151; 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> &#151; 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&amp; operator=(const Animal&amp; rhs);
<A NAME="33126"></A>
  Lizard&amp; operator=(const Lizard&amp; 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&amp;
<A NAME="32783"></A>
Animal *pAnimal1 = &amp;liz1;
Animal *pAnimal2 = &amp;liz2;
<A NAME="76575"></A>
...
<A NAME="76576"></A>
*pAnimal1 = *pAnimal2;                          // calls operator= taking
                                                 // a const Animal&amp;</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&amp; Lizard::operator=(const Animal&amp; rhs)
{
  return operator=(dynamic_cast&lt;const Lizard&amp;&gt;(rhs));
}</PRE>
</UL>

⌨️ 快捷键说明

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