📄 ei17.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 17: Check for assignment to self in operator=</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 3; setCurrentMax(3);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI17_DIR.HTM";
var dingtext = "Item E17, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E17: Check for assignment to self in operator=" -->
<A NAME="2264"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI16_FR.HTM" TARGET="_top">Item 16: Assign to all data members in operator=.</A> <BR> Continue to <A HREF="./EDESGNFR.HTM" TARGET="_top">Classes and Functions: Design and Declaration</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 17: Check for assignment to self in <CODE>operator=</CODE>.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P><A NAME="2265"></A>
<P><A NAME="dingp2"></A>
An assignment to self occurs when you do something like <NOBR>this:<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="2266"></A>
<UL><PRE><A NAME="p72"></A>class X { ... };
</PRE>
</UL><A NAME="2267"></A>
<UL><PRE>X a;
</PRE>
</UL><A NAME="2268"></A>
<UL><PRE>a = a; // a is assigned to itself
</PRE>
</UL><A NAME="2269"></A>
<P><A NAME="dingp3"></A>
This looks like a silly thing to do, but it's perfectly legal, so don't doubt for a moment that programmers do it. More importantly, assignment to self can appear in this more benign-looking <NOBR>form:<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="2270"></A>
<UL><PRE>a = b;
</PRE>
</UL><A NAME="2271"></A>
<P><A NAME="dingp4"></A>
If <CODE>b</CODE> is another name for <CODE>a</CODE> (for example, a reference that has been initialized to <CODE>a</CODE>), then this is also an assignment to self, though it doesn't outwardly look like it. This is an example of <I>aliasing</I>: having two or more names for the same underlying object. As you'll see at the end of this Item, aliasing can crop up in any number of nefarious disguises, so you need to take it into account any time you write a <NOBR>function.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="2272"></A>
<P><A NAME="dingp5"></A>
Two good reasons exist for taking special care to cope with possible aliasing in assignment operator(s). The lesser of them is efficiency. If you can detect an assignment to self at the top of your assignment operator(s), you can return right away, possibly saving a lot of work that you'd otherwise have to go through to implement assignment. For example, <A HREF="./EI16_FR.HTM#2225" TARGET="_top">Item 16</A> points out that a proper assignment operator in a derived class must call an assignment operator for each of its base classes, and those classes might themselves be derived classes, so skipping the body of an assignment operator in a derived class might save a large number of other function <NOBR>calls.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="2273"></A>
<P><A NAME="dingp6"></A>
A more important reason for checking for assignment to self is to ensure correctness. Remember that an assignment operator must typically free the resources allocated to an object (i.e., get rid of its old value) before it can allocate the new resources corresponding to its new value. When assigning to self, this freeing of resources can be disastrous, because the old resources might be needed during the process of allocating the new <NOBR>ones.<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="2274"></A>
<P><A NAME="dingp7"></A>
Consider assignment of <CODE>String</CODE> objects, where the assignment operator fails to check for assignment to self:<SCRIPT>create_link(7);</SCRIPT>
<A NAME="222604"></A>
<UL><PRE>class String {
public:
String(const char *value); // see <A HREF="./EI11_FR.HTM#2042" TARGET="_top">Item 11</A> for
// function definition
</PRE>
</UL><A NAME="223599"></A>
<UL><PRE>
~String(); // see <A HREF="./EI11_FR.HTM#2042" TARGET="_top">Item 11</A> for
// function definition
...
</PRE>
</UL><A NAME="223602"></A>
<UL><PRE> String& operator=(const String& rhs);
</PRE>
</UL><A NAME="223600"></A>
<UL><PRE><A NAME="p73"></A>private:
char *data;
};
</PRE>
</UL><A NAME="2279"></A>
<UL><PRE>// an assignment operator that omits a check
// for assignment to self
String& String::operator=(const String& rhs)
{
delete [] data; // delete old memory
</PRE>
</UL><A NAME="2280"></A>
<UL><PRE> // allocate new memory and copy rhs's value into it
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
</PRE>
</UL><A NAME="2281"></A>
<UL><PRE> return *this; // see <A HREF="./EI15_FR.HTM#2182" TARGET="_top">Item 15</A>
}
</PRE>
</UL></P><A NAME="2282"></A>
<P><A NAME="dingp8"></A>
Consider now what happens in this case:<SCRIPT>create_link(8);</SCRIPT>
<A NAME="2283"></A>
<UL><PRE>String a = "Hello";
</PRE>
</UL><A NAME="2284"></A>
<UL><PRE>a = a; // same as a.operator=(a)
</PRE>
</UL><A NAME="2285"></A>
<P><A NAME="dingp9"></A>
Inside the assignment operator, <CODE>*this</CODE> and <CODE>rhs</CODE> seem to be different objects, but in this case they happen to be different names for the same object. You can envision it like <NOBR>this:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073A5.GIF" BORDER=0></SPAN>
<A NAME="2286"></A>
<P><A NAME="dingp10"></A>
The first thing the assignment operator does is use <CODE>delete</CODE> on <CODE>data</CODE>, and the result is the following state of <NOBR>affairs:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B5.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_073B5.GIF" BORDER=0></SPAN>
<A NAME="2287"></A>
<P><A NAME="dingp11"></A>
Now when the assignment operator tries to do a <CODE>strlen</CODE> on <CODE>rhs.data</CODE>, the results are undefined. This is because <CODE>rhs.data</CODE> was deleted when <CODE>data</CODE> was deleted, which happened because <CODE>data</CODE>, <CODE>this->data</CODE>, and <CODE>rhs.data</CODE> are all the same pointer! From this point on, things can only get <NOBR>worse.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -