📄 ei29.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 29: Avoid returning "handles" to internal data</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 = 2; setCurrentMax(2);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT>
var dingbase = "EI29_DIR.HTM";
var dingtext = "Item E29, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E29: Avoid returning 'handles' to internal data." -->
<A NAME="6490"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EIMPL_FR.HTM" TARGET="_top">Implementation</A> <BR> Continue to <A HREF="./EI30_FR.HTM" TARGET="_top">Item 30: Avoid member functions that return non-const pointers or references to members less accessible than themselves.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 29: Avoid returning "handles" to internal data.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="6492"></A>
<P><A NAME="dingp2"></A>
A scene from an object-oriented <NOBR>romance:<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="6493"></A>
<UL><P><A NAME="dingp3"></A>
Object A: Darling, don't ever <NOBR>change!<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="6494"></A>
<P><A NAME="dingp4"></A>
Object B: Don't worry, dear, I'm <CODE>const</CODE>.<SCRIPT>create_link(4);</SCRIPT>
</P>
<A NAME="6495"></A>
</UL><P><A NAME="dingp5"></A>
Yet just as in real life, A wonders, "Can B be trusted?" And just as in real life, the answer often hinges on B's nature: the constitution of its member <NOBR>functions.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="19293"></A>
<P><A NAME="dingp6"></A>
<A NAME="p124"></A>Suppose B is a constant <CODE>String</CODE> <NOBR>object:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="18182"></A>
<UL><PRE>class String {
public:
String(const char *value); // see <A HREF="./EI11_FR.HTM#2042" TARGET="_top">Item 11</A> for pos-
~String(); // sible implementations;
// see <A HREF="../MEC/MI5_FR.HTM#5970" TARGET="_top">Item M5</A> for comments
// on the first constructor
</PRE>
</UL><A NAME="6500"></A>
<UL><PRE>
operator char *() const; // convert String -> char*;
// see also <A HREF="../MEC/MI5_FR.HTM#5970" TARGET="_top">Item M5</A>
...
private:
char *data;
};
</PRE>
</UL><A NAME="6502"></A>
<UL><PRE>
const String B("Hello World"); // B is a const object
</PRE>
</UL><A NAME="6503"></A>
<P><A NAME="dingp7"></A>
Because <CODE>B</CODE> is <CODE>const</CODE>, it had better be the case that the value of <CODE>B</CODE> now and evermore is "Hello World". Of course, this supposes that programmers working with <CODE>B</CODE> are playing the game in a civilized fashion. In particular, it depends on the fact that nobody is "casting away the constness" of <CODE>B</CODE> through nefarious ploys such as this (see <A HREF="./EI21_FR.HTM#6003" TARGET="_top">Item 21</A>):<SCRIPT>create_link(7);</SCRIPT>
</P>
<A NAME="6508"></A>
<UL><PRE>
String& alsoB = // make alsoB another name
const_cast<String&>(B); // for B, but without the
// constness
</PRE>
</UL><A NAME="6509"></A>
<P><A NAME="dingp8"></A>
Given that no one is doing such evil deeds, however, it seems a safe bet that <CODE>B</CODE> will never change. Or does it? Consider this sequence of <NOBR>events:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="6510"></A>
<UL><PRE>
char *str = B; // calls B.operator char*()
</PRE>
</UL><A NAME="6511"></A>
<UL><PRE>
strcpy(str, "Hi Mom"); // modifies what str
// points to
</PRE>
</UL><A NAME="6512"></A>
<P><A NAME="dingp9"></A>
Does <CODE>B</CODE> still have the value "Hello World", or has it suddenly mutated into something you might say to your mother? The answer depends entirely on the implementation of <CODE>String::operator</CODE> <CODE>char*</CODE>.<SCRIPT>create_link(9);</SCRIPT>
</P>
<A NAME="6513"></A>
<P><A NAME="dingp10"></A>
Here's a careless implementation, one that does the wrong thing. However, it does it very efficiently, which is why so many programmers fall into this <NOBR>trap:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="6515"></A>
<UL><PRE>// a fast, but incorrect implementation
inline String::operator char*() const
{ return data; }
</PRE>
</UL><A NAME="6517"></A>
<P><A NAME="dingp11"></A>
The flaw in this function is that it's returning a "handle" — in this case, a pointer — to information that should be hidden inside the <CODE>String</CODE> object on which the function is invoked. That handle gives callers unrestricted access to what the private field <CODE>data</CODE> points to. In other words, after the <NOBR>statement<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="6518"></A>
<UL><PRE>char *str = B;
</PRE>
</UL><A NAME="6562"></A>
<P><A NAME="dingp12"></A>
<A NAME="p125"></A>the situation looks like <NOBR>this:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_125A5.GIF" BORDER=0></SPAN>
<A NAME="6563"></A>
<P><A NAME="dingp13"></A>
Clearly, any modification to the memory pointed to by <CODE>str</CODE> will also change the effective value of <CODE>B</CODE>. Thus, even though <CODE>B</CODE> is declared <CODE>const</CODE>, and even though only <CODE>const</CODE> member functions are invoked on <CODE>B</CODE>, <CODE>B</CODE> might still acquire different values as the program runs. In particular, if <CODE>str</CODE> modifies what it points to, <CODE>B</CODE> will also <NOBR>change.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="6564"></A>
<P><A NAME="dingp14"></A>
There's nothing inherently wrong with the way <CODE>String::operator char*</CODE> is written. What's troublesome is that it can be applied to constant objects. If the function weren't declared <CODE>const</CODE>, there would be no problem, because it couldn't be applied to objects like <CODE>B</CODE>.<SCRIPT>create_link(14);</SCRIPT>
</P>
<A NAME="6565"></A>
<P><A NAME="dingp15"></A>
Yet it seems perfectly reasonable to turn a <CODE>String</CODE> object, even a constant one, into its equivalent <CODE>char*</CODE>, so you'd like to keep this function <CODE>const</CODE>. If you want to do that, you must rewrite your implementation to avoid returning a handle to the object's internal <NOBR>data:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="6566"></A>
<UL><PRE>// a slower, but safer implementation
inline String::operator char*() const
{
char *copy = new char[strlen(data) + 1];
strcpy(copy, data);
</PRE>
</UL><A NAME="6568"></A>
<UL><PRE> return copy;
</PRE>
</UL><A NAME="6569"></A>
<UL><PRE>}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -