📄 ec5.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 | Chapter 5: Classes and Functions: Implementation</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 2; setCurrentMax(2);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EC5_DIR.HTM";
var dingtext = "EC++ Class/Func Impl, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- one image occurring in Chapter 5 -->
<!-- SectionName="EC++ Chapter Intro: Impl of Classes/Functions" -->
<A NAME="6485"></A><A NAME="p123"></A><A NAME="26354"></A><A NAME="6486"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EC4_FR.HTM#6429" TARGET="_top">Item 28: Partition the global namespace.</A> <BR>Continue to <A HREF="#6490">Item 29: Avoid returning "handles" to internal data.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="egtitle">Classes and Functions: Implementation</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<P><A NAME="dingp2"></A>
Because C++ is strongly typed, coming up with appropriate definitions for your classes and templates and appropriate declarations for your functions is the lion's share of the battle. Once you've got those right, it's hard to go wrong with the template, class, and function implementations. Yet, somehow, people manage to do <NOBR>it.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="6487"></A>
<P><A NAME="dingp3"></A>
Some problems arise from inadvertently violating abstraction: accidentally allowing implementation details to peek out from behind the class and function boundaries that are supposed to contain them. Others originate in confusion over the length of an object's lifetime. Still others stem from premature optimization, typically traceable to the seductive nature of the <CODE>inline</CODE> keyword. Finally, some implementation strategies, while fine on a local scale, result in levels of coupling between source files that can make it unacceptably costly to rebuild large <NOBR>systems.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="6488"></A>
<P><A NAME="dingp4"></A>
Each of these problems, as well as others like them, can be avoided if you know what to watch out for. The items that follow identify some situations in which you need to be especially <NOBR>vigilant.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<!-- SectionName="E29: Avoid returning 'handles' to internal data" -->
<A NAME="6490"></A><A NAME="6492"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#26354">Classes and Functions: Implementation</A>
<BR>Continue to <A HREF="#6599">Item 30: Avoid member functions that return non-const pointers or references to members less accessible than themselves.</A></FONT></DIV>
<P><A NAME="dingp5"></A><FONT ID="eititle">Item 29: Avoid returning "handles" to internal data.</FONT><SCRIPT>create_link(5);</SCRIPT>
</P>
<P><A NAME="dingp6"></A>
A scene from an object-oriented <NOBR>romance:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="6493"></A>
<UL><P><A NAME="dingp7"></A>
Object A: Darling, don't ever <NOBR>change!<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="6494"></A>
<P><A NAME="dingp8"></A>
Object B: Don't worry, dear, I'm <CODE>const</CODE>.<SCRIPT>create_link(8);</SCRIPT>
</P>
<A NAME="6495"></A>
</UL><P><A NAME="dingp9"></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(9);</SCRIPT>
</NOBR></P>
<A NAME="19293"></A>
<P><A NAME="dingp10"></A>
<A NAME="p124"></A>Suppose B is a constant <CODE>String</CODE> <NOBR>object:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="18182"></A>
<UL><PRE>class String {
public:
String(const char *value); // see <A HREF="./EC3_FR.HTM#2042" TARGET="_top">Item 11</A> for pos-
~String(); // sible implementations;
// see <A HREF="../MEC/MC2_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/MC2_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="dingp11"></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="./EC4_FR.HTM#6003" TARGET="_top">Item 21</A>):<SCRIPT>create_link(11);</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="dingp12"></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(12);</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="dingp13"></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(13);</SCRIPT>
</P>
<A NAME="6513"></A>
<P><A NAME="dingp14"></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(14);</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="dingp15"></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(15);</SCRIPT>
</NOBR></P>
<A NAME="6518"></A>
<UL><PRE>char *str = B;
</PRE>
</UL><A NAME="6562"></A>
<P><A NAME="dingp16"></A>
<A NAME="p125"></A>the situation looks like <NOBR>this:<SCRIPT>create_link(16);</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="dingp17"></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(17);</SCRIPT>
</NOBR></P>
<A NAME="6564"></A>
<P><A NAME="dingp18"></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(18);</SCRIPT>
</P>
<A NAME="6565"></A>
<P><A NAME="dingp19"></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(19);</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>}
</PRE>
</UL><A NAME="6570"></A>
<P><A NAME="dingp20"></A>
This implementation is safe, because it returns a pointer to memory that contains a <I>copy</I> of the data to which the <CODE>String</CODE> object points; there is no way to change the value of the <CODE>String</CODE> object through the pointer returned by this function. As usual, such safety commands a price: this version of <CODE>String::operator</CODE> <CODE>char*</CODE> is slower than the simple version above, and callers of this function must remember to use <CODE>delete</CODE> on the pointer that's <NOBR>returned.<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="6571"></A>
<P><A NAME="dingp21"></A>
If you think this version of <CODE>operator</CODE> <CODE>char*</CODE> is too slow, or if the potential memory leak makes you nervous (as well it should), a slightly different tack is to return a pointer to <I>constant</I> <CODE>char</CODE>s:<SCRIPT>create_link(21);</SCRIPT>
</P>
<A NAME="6574"></A>
<UL><PRE><A NAME="p126"></A>class String {
public:
operator const char *() const;
</PRE>
</UL><A NAME="6575"></A>
<UL><PRE> ...
};
</PRE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -