📄 ei11.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 11: Declare a copy constructor and an assignment operator for classes with dynamically allocated memory</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 = "EI11_DIR.HTM";
var dingtext = "Item E11, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E11: Handle copying in classes with pointers" -->
<A NAME="2042"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./ECTOR_FR.HTM" TARGET="_top">Constructors, Destructors, and Assignment Operators</A> <BR> Continue to <A HREF="./EI12_FR.HTM" TARGET="_top">Item 12: Prefer initialization to assignment in constructors.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 11: Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P><A NAME="2043"></A>
<P><A NAME="dingp2"></A>
Consider a class for representing <CODE>String</CODE> objects:<SCRIPT>create_link(2);</SCRIPT>
<A NAME="222600"></A>
<UL><PRE>// a poorly designed String class
class String {
public:
String(const char *value);
~String();
<A NAME="222601"></A>
... // no copy ctor or operator=
<A NAME="28434"></A>
private:
char *data;
};
<A NAME="2046"></A>
<A NAME="p50"></A>String::String(const char *value)
{
if (value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
}
else {
data = new char[1];
*data = '\0';
}
}
<A NAME="2047"></A>
inline String::~String() { delete [] data; }
</PRE>
</UL></P><A NAME="2048"></A>
<P><A NAME="dingp3"></A>
Note that there is no assignment operator or copy constructor declared in this class. As you'll see, this has some unfortunate <NOBR>consequences.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="2049"></A>
<P><A NAME="dingp4"></A>If you make these object <NOBR>definitions,<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="2050"></A>
<UL><PRE>String a("Hello");
String b("World");
</PRE>
</UL><A NAME="2051"></A><P><A NAME="dingp5"></A>
the situation is as shown <NOBR>below:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050A5.GIF" BORDER=0></SPAN>
<A NAME="2052"></A>
<P><A NAME="dingp6"></A>
Inside object <CODE>a</CODE> is a pointer to memory containing the character string "Hello". Separate from that is an object <CODE>b</CODE> containing a pointer to the character string "World". If you now perform an <NOBR>assignment,<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="2053"></A>
<UL><PRE>b = a;
</PRE>
</UL><A NAME="2054"></A>
<P><A NAME="dingp7"></A>
there is no client-defined <CODE>operator=</CODE> to call, so C++ generates and calls the default assignment operator instead (see <A HREF="./EI45_FR.HTM#8160" TARGET="_top">Item 45</A>). This default assignment operator performs memberwise assignment from the members of <CODE>a</CODE> to the members of <CODE>b</CODE>, which for pointers (<CODE>a.data</CODE> and <CODE>b.data</CODE>) is just a bitwise copy. The result of this assignment is shown <NOBR>below.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B5.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_050B5.GIF" BORDER=0></SPAN>
<A NAME="2055"></A>
<P><A NAME="dingp8"></A>
<A NAME="p51"></A>There are at least two problems with this state of affairs. First, the memory that <CODE>b</CODE> used to point to was never deleted; it is lost forever. This is a classic example of how a memory leak can arise. Second, both <CODE>a</CODE> and <CODE>b</CODE> now contain pointers to the same character string. When one of them goes out of scope, its destructor will delete the memory still pointed to by the other. For <NOBR>example:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="2056"></A>
<UL><PRE>
String a("Hello"); // define and construct a
</PRE>
</UL><A NAME="2057"></A>
<UL><PRE>
{ // open new scope
String b("World"); // define and construct b
</PRE>
</UL><A NAME="2058"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="2059"></A>
<UL><PRE>
b = a; // execute default op=,
// lose b's memory
</PRE>
</UL><A NAME="2060"></A>
<UL><PRE>
} // close scope, call b's
// destructor
</PRE>
</UL><A NAME="2061"></A>
<UL><PRE>
String c = a; // c.data is undefined!
// a.data is already deleted
</PRE>
</UL><A NAME="2062"></A>
<P><A NAME="dingp9"></A>
The last statement in this example is a call to the copy constructor, which also isn't defined in the class, hence will be generated by C++ in the same manner as the assignment operator (again, see <A HREF="./EI45_FR.HTM#8160" TARGET="_top">Item 45</A>) and with the same behavior: bitwise copy of the underlying pointers. That leads to the same kind of problem, but without the worry of a memory leak, because the object being initialized can't yet point to any allocated memory. In the case of the code above, for example, there is no memory leak when <CODE>c.data</CODE> is initialized with the value of <CODE>a.data</CODE>, because <CODE>c.data</CODE> doesn't yet point anywhere. However, after <CODE>c</CODE> is initialized with <CODE>a</CODE>, both <CODE>c.data</CODE> and <CODE>a.data</CODE> point to the same place, so that place will be deleted twice: once when <CODE>c</CODE> is destroyed, once again when <CODE>a</CODE> is <NOBR>destroyed.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="2063"></A>
<P><A NAME="dingp10"></A>
The case of the copy constructor differs a little from that of the assignment operator, however, because of the way it can bite you: pass-by-value. Of course, <A HREF="./EI22_FR.HTM#6133" TARGET="_top">Item 22</A> demonstrates that you should only rarely pass objects by value, but consider this <NOBR>anyway:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="2064"></A>
<UL><PRE>void doNothing(String localString) {}
</PRE>
</UL><A NAME="2065"></A>
<UL><PRE>String s = "The Truth Is Out There";
</PRE>
</UL><A NAME="2066"></A>
<UL><PRE>doNothing(s);
</PRE>
</UL><A NAME="2067"></A>
<P><A NAME="dingp11"></A>
Everything looks innocuous enough, but because <CODE>localString</CODE> is passed by value, it must be initialized from <CODE>s</CODE> via the (default) copy constructor. Hence, <CODE>localString</CODE> has a copy of the <I>pointer</I> that is inside <CODE>s</CODE>. When <CODE>doNothing</CODE> finishes executing, <CODE>localString</CODE> goes out of <A NAME="p52"></A>scope, and its destructor is called. The end result is by now familiar: <CODE>s</CODE> contains a pointer to memory that <CODE>localString</CODE> has already <NOBR>deleted.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="2068"></A>
<P><A NAME="dingp12"></A>
By the way, the result of using <CODE>delete</CODE> on a pointer that has already been deleted is undefined, so even if <CODE>s</CODE> is never used again, there could well be a problem when it goes out of <NOBR>scope.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="2069"></A>
<P><A NAME="dingp13"></A>
The solution to these kinds of pointer aliasing problems is to write your own versions of the copy constructor and the assignment operator if you have any pointers in your class. Inside those functions, you can either copy the pointed-to data structures so that every object has its own copy, or you can implement some kind of reference-counting scheme (see <A HREF="../MEC/MI29_FR.HTM#6073" TARGET="_top">Item M29</A>) to keep track of how many objects are currently pointing to a particular data structure. The reference-counting approach is more complicated, and it calls for extra work inside the constructors and destructors, too, but in some (though by no means all) applications, it can result in significant memory savings and substantial increases in <NOBR>speed.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="14901"></A>
<P><A NAME="dingp14"></A>
For some classes, it's more trouble than it's worth to implement copy constructors and assignment operators, especially when you have reason to believe that your clients won't make copies or perform assignments. The examples above demonstrate that omitting the corresponding member functions reflects poor design, but what do you do if writing them isn't practical, either? Simple: you follow this Item's advice. You <I>declare</I> the functions (<CODE>private</CODE>, as it turns out), but you don't define (i.e., implement) them at all. That prevents clients from calling them, and it prevents compilers from generating them, too. For details on this nifty trick, see <A HREF="./EI27_FR.HTM#6406" TARGET="_top">Item 27</A>.<SCRIPT>create_link(14);</SCRIPT>
</P>
<A NAME="2070"></A>
<P><A NAME="dingp15"></A>
One more thing about the <CODE>String</CODE> class I used in this Item. In the constructor body, I was careful to use <CODE>[]</CODE> with <CODE>new</CODE> both times I called it, even though in one of the places I wanted only a single object. As described in <A HREF="./EI5_FR.HTM#1869" TARGET="_top">Item 5</A>, it's essential to employ the same form in corresponding applications of <CODE>new</CODE> and <CODE>delete</CODE>, so I was careful to be consistent in my uses of <CODE>new</CODE>. This is something you do <I>not</I> want to forget. <I>Always</I> make sure that you use <CODE>[]</CODE> with <CODE>delete</CODE> if and only if you used <CODE>[]</CODE> with the corresponding use of <CODE>new</CODE>.<SCRIPT>create_link(15);</SCRIPT>
</P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./ECTOR_FR.HTM" TARGET="_top">Constructors, Destructors, and Assignment Operators</A> <BR> Continue to <A HREF="./EI12_FR.HTM" TARGET="_top">Item 12: Prefer initialization to assignment in constructors.</A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -