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

📄 ei15.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 15: Have operator= return a reference to *this</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

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

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E15: Have operator= return a reference to *this." -->
<A NAME="2182"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI14_FR.HTM" TARGET="_top">Item 14: Make sure base classes have virtual destructors.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI16_FR.HTM" TARGET="_top">Item 16: Assign to all data members in operator=.</A></FONT></DIV>

<P><A NAME="dingp1"></A><FONT ID="eititle">Item 15: &nbsp;Have <CODE>operator=</CODE> return a reference to <CODE>*this</CODE>.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P><A NAME="2183"></A>
<P><A NAME="dingp2"></A>
<NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=bjarne" onMouseOver = "self.status = 'Bjarne Strostrup'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">Bjarne</NOBR> Stroustrup</A>, the designer of C++, went to a lot of trouble to ensure that user-defined types would mimic the built-in types as closely as possible. That's why you can overload operators, write type conversion functions (see <A HREF="../MEC/MI5_FR.HTM#5970" TARGET="_top">Item M5</A>), take control of assignment and copy construction, etc. After so much effort on his part, the least you can do is keep the ball <NOBR>rolling.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="2184"></A>
<P><A NAME="dingp3"></A>
Which brings us to assignment. With the built-in types, you can chain assignments together, like <NOBR>so:<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="2185"></A>
<UL><PRE>int w, x, y, z;
</PRE>
</UL><A NAME="2186"></A>
<UL><PRE>w = x = y = z = 0;
</PRE>
</UL><A NAME="2187"></A>
<P><A NAME="dingp4"></A>
As a result, you should be able to chain together assignments for user-defined types, <NOBR>too:<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="2190"></A>
<UL><PRE>
string w, x, y, z;               // string is "user-defined"
                                 // by the standard C++
                                 // library (see <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A>)
</PRE>
</UL><A NAME="2191"></A>
<UL><PRE>w = x = y = z = "Hello";
</PRE>
</UL><A NAME="2192"></A>
<P><A NAME="dingp5"></A>
As fate would have it, the assignment operator is right-associative, so the assignment chain is parsed like <NOBR>this:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="2193"></A>
<UL><PRE>w = (x = (y = (z = "Hello")));
</PRE>
</UL><A NAME="1683"></A>
<P><A NAME="dingp6"></A>
<A NAME="p65"></A>It's worthwhile to write this in its completely equivalent functional form. Unless you're a closet LISP programmer, this example should make you grateful for the ability to define infix <NOBR>operators:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="2195"></A>
<P>
<UL><PRE>w.operator=(x.operator=(y.operator=(z.operator=("Hello"))));
</PRE>
</UL><A NAME="2196"></A>
<P><A NAME="dingp7"></A>
This form is illustrative because it emphasizes that the argument to <CODE>w.operator=</CODE>, <CODE>x.operator=</CODE>, and <CODE>y.operator=</CODE> is the return value of a previous call to <CODE>operator=</CODE>. As a result, the return type of <CODE>operator=</CODE> must be acceptable as an input to the function itself. For the default version of <CODE>operator=</CODE> in a class <CODE>C</CODE>, the signature of the function is as follows (see <A HREF="./EI45_FR.HTM#8160" TARGET="_top">Item 45</A>):<SCRIPT>create_link(7);</SCRIPT>
</P>
<A NAME="2197"></A>
<UL><PRE>C&amp; C::operator=(const C&amp;);
</PRE>
</UL><A NAME="2198"></A>
<P><A NAME="dingp8"></A>
You'll almost always want to follow this convention of having <CODE>operator=</CODE> both take and return a reference to a class object, although at times you may overload <CODE>operator=</CODE> so that it takes different argument types. For example, the standard <CODE>string</CODE> type provides two different versions of the assignment <NOBR>operator:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="2199"></A>
<UL><PRE>
string&amp;                            // assign a string
operator=(const string&amp; rhs);      // to a string
</PRE>
</UL><A NAME="2200"></A>
<UL><PRE>
string&amp;                            // assign a char*
operator=(const char *rhs);        // to a string
</PRE>
</UL><A NAME="2201"></A>
<P><A NAME="dingp9"></A>
Notice, however, that even in the presence of overloading, the return type is a reference to an object of the <NOBR>class.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="2202"></A>
<P><A NAME="dingp10"></A>
A common error amongst new C++ programmers is to have <CODE>operator=</CODE> return <CODE>void</CODE>, a decision that seems reasonable until you realize it prevents chains of assignment. So don't do <NOBR>it.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="31197"></A>
<P><A NAME="dingp11"></A>
Another common error is to have <CODE>operator=</CODE> return a reference to a <CODE>const</CODE> object, like <NOBR>this:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="31133"></A>
<UL><PRE>class Widget {
public:
  ...                                            // note
  const Widget&amp; operator=(const Widget&amp; rhs);    // const
  ...                                            // return
};                                               // type
</PRE>
</UL><A NAME="31136"></A>
<P><A NAME="dingp12"></A>
The usual motivation is to prevent clients from doing silly things like <NOBR>this:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="219527"></A>
<UL><PRE><A NAME="p66"></A>Widget w1, w2, w3;
</PRE>
</UL><A NAME="219531"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="219532"></A>
<UL><PRE>
(w1 = w2) = w3;         // assign w2 to w1, then w3 to
                        // the result! (Giving Widget's
                        // operator= a const return value
                        // prevents this from compiling.)
</PRE>
</UL><A NAME="31140"></A>
<P><A NAME="dingp13"></A>
Silly this may be, but not so silly that it's prohibited for the built-in <NOBR>types:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="31143"></A>
<UL><PRE>int i1, i2, i3;
</PRE>
</UL><A NAME="31144"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="31145"></A>
<UL><PRE>
(i1 = i2) = i3;                // legal! assigns i2 to
                               // i1, then i3 to i1!
</PRE>
</UL><A NAME="2203"></A>
<P><A NAME="dingp14"></A>
I know of no practical use for this kind of thing, but if it's good enough for the <CODE>int</CODE>s, it's good enough for me and my classes. It should be good enough for you and yours, too. Why introduce gratuitous incompatibilities with the conventions followed by the built-in <NOBR>types?<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="31164"></A>
<P><A NAME="dingp15"></A>
Within an assignment operator bearing the default signature, there are two obvious candidates for the object to be returned: the object on the left hand side of the assignment (the one pointed to by <CODE>this</CODE>) and the object on the right-hand side (the one named in the parameter list). Which is <NOBR>correct?<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="2204"></A>
<P><A NAME="dingp16"></A>
Here are the possibilities for a <CODE>String</CODE> class (a class for which you'd definitely want to write an assignment operator, as explained in <A HREF="./EI11_FR.HTM#2042" TARGET="_top">Item 11</A>):<SCRIPT>create_link(16);</SCRIPT>
</P>
<A NAME="2205"></A>
<UL><PRE>String&amp; String::operator=(const String&amp; rhs)
{
</PRE>
</UL><A NAME="2206"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="2207"></A>
<UL><PRE>
  return *this;            // return reference
                           // to left-hand object
}
</PRE>
</UL><A NAME="2208"></A>
<UL><PRE>String&amp; String::operator=(const String&amp; rhs)
{
</PRE>
</UL><A NAME="2209"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="2210"></A>
<UL><PRE>
  return rhs;              // return reference to
                           // right-hand object
}
</PRE>
</UL><A NAME="2211"></A>
<P><A NAME="dingp17"></A>
This might strike you as a case of six of one versus a half a dozen of the other, but there are important <NOBR>differences.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="2212"></A>
<P><A NAME="dingp18"></A>
<A NAME="p67"></A>First, the version returning <CODE>rhs</CODE> won't compile. That's because <CODE>rhs</CODE> is a reference-to-<CODE><I>const</I></CODE>-<CODE>String</CODE>, but <CODE>operator=</CODE> returns a reference-to-<CODE>String</CODE>. Compilers will give you no end of grief for trying to return a reference-to-non-<CODE>const</CODE> when the object itself is <CODE>const</CODE>. That seems easy enough to get around, however &#151; just redeclare <CODE>operator=</CODE> like <NOBR>this:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="2213"></A>
<UL><PRE>String&amp; String::operator=(String&amp; rhs)   { ... }
</PRE>
</UL><A NAME="2214"></A>
<P><A NAME="dingp19"></A>
Alas, now the client code won't compile! Look again at the last part of the original chain of <NOBR>assignments:<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="2216"></A>
<UL><PRE>x = "Hello";                     // same as x.op=("Hello");
</PRE>
</UL><A NAME="2217"></A>
<P><A NAME="dingp20"></A>
Because the right-hand argument of the assignment is not of the correct type &#151; it's a <CODE>char</CODE> array, not a <CODE>String</CODE> &#151; compilers would have to create a temporary <CODE>String</CODE> object (via the <CODE>String</CODE> constructor &#151; see <A HREF="../MEC/MI19_FR.HTM#41177" TARGET="_top">Item M19</A>) to make the call succeed. That is, they'd have to generate code roughly equivalent to <NOBR>this:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="2218"></A>
<UL><PRE>const String temp("Hello");      // create temporary
</PRE>
</UL><A NAME="2219"></A>
<UL><PRE>x = temp;                        // pass temporary to op=
</PRE>
</UL><A NAME="15466"></A>
<P><A NAME="dingp21"></A>
Compilers are willing to create such a temporary (unless the needed constructor is <CODE>explicit</CODE> &#151; see <A HREF="./EI19_FR.HTM#5887" TARGET="_top">Item 19</A>), but note that the temporary object is <CODE>const</CODE>. This is important, because it prevents you from accidentally passing a temporary into a function that modifies its parameter. If that were allowed, programmers would be surprised to find that only the compiler-generated temporary was modified, not the argument they actually provided at the call site. (We know this for a fact, because early versions of C++ allowed these kinds of temporaries to be generated, passed, and modified, and the result was a lot of surprised <NOBR>programmers.)<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="15467"></A>
<P><A NAME="dingp22"></A>
Now we can see why the client code above won't compile if <CODE>String</CODE>'s <CODE>operator=</CODE> is declared to take a reference-to-non-<CODE>const</CODE> <CODE>String</CODE>: it's never legal to pass a <CODE>const</CODE> object to a function that fails to declare the corresponding parameter <CODE>const</CODE>. That's just simple <CODE>const</CODE>-correctness.<SCRIPT>create_link(22);</SCRIPT>
</P>
<A NAME="2224"></A>
<P><A NAME="dingp23"></A>
You thus find yourself in the happy circumstance of having no choice whatsoever: you'll always want to define your assignment operators in such a way that they return a reference to their left-hand argument, <CODE>*this</CODE>. If you do anything else, you prevent chains of assignments, you prevent implicit type conversions at call sites, or <NOBR>both.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI14_FR.HTM" TARGET="_top">Item 14: Make sure base classes have virtual destructors.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI16_FR.HTM" TARGET="_top">Item 16: Assign to all data members in operator=.</A></FONT></DIV>
</BODY>
</HTML>

⌨️ 快捷键说明

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