📄 ec5.htm
字号:
</UL><A NAME="6577"></A>
<UL><PRE>inline String::operator const char*() const
{ return data; }
</PRE>
</UL><A NAME="6578"></A>
<P><A NAME="dingp22"></A>
This function is fast and safe, and, though it's not the same as the function you originally specified, it suffices for most applications. It's also the moral equivalent of the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver = "self.status = 'ISO ANSI Standardization Committee'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standardization committee's</A> solution to the <CODE>string</CODE>/<CODE>char*</CODE> conundrum: the standard <CODE>string</CODE> type contains a member function <CODE>c_str</CODE> that returns a <CODE>const</CODE> <CODE>char*</CODE> version of the <CODE>string</CODE> in question. For more information on the standard <CODE>string</CODE> type, turn to <A HREF="./EC7_FR.HTM#8392" TARGET="_top">Item 49</A>.<SCRIPT>create_link(22);</SCRIPT>
</P>
<A NAME="6584"></A>
<P><A NAME="dingp23"></A>
A pointer isn't the only way to return a handle to internal data. References are just as easy to abuse. Here's a common way to do it, again using the <CODE>String</CODE> <NOBR>class:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="6586"></A>
<UL><PRE>class String {
public:
</PRE>
</UL><A NAME="18188"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="6588"></A>
<UL><PRE> char& operator[](int index) const
{ return data[index]; }
</PRE>
</UL><A NAME="18191"></A>
<UL><PRE>private:
char *data;
};
</PRE>
</UL><A NAME="6590"></A>
<UL><PRE>String s = "I'm not constant";
</PRE>
</UL><A NAME="6591"></A>
<UL><PRE>
s[0] = 'x'; // fine, s isn't const
</PRE>
</UL><A NAME="6592"></A>
<UL><PRE>const String cs = "I'm constant";
</PRE>
</UL><A NAME="6593"></A>
<UL><PRE>
cs[0] = 'x'; // this modifies the const
// string, but compilers
// won't notice
</PRE>
</UL><A NAME="6595"></A>
<P><A NAME="dingp24"></A>
Notice how <CODE>String::operator[]</CODE> returns its result by reference. That means that the caller of this function gets back <I>another name</I> for the internal element <CODE>data[index]</CODE>, and that other name can be used to modify the internal data of the supposedly constant object. This is the same problem you saw before, but this time the culprit is a reference as a return value, not a <NOBR>pointer.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="19311"></A>
<P><A NAME="dingp25"></A>
<A NAME="p127"></A>The general solutions to this kind of problem are the same as they were for pointers: either make the function non-<CODE>const</CODE>, or rewrite it so that no handle is returned. For a solution to this <I>particular</I> problem — how to write <CODE>String::operator[]</CODE> so that it works for both <CODE>const</CODE> and non-<CODE>const</CODE> objects — see <A HREF="./EC4_FR.HTM#6003" TARGET="_top">Item 21</A>.<SCRIPT>create_link(25);</SCRIPT>
</P>
<A NAME="18232"></A>
<P><A NAME="dingp26"></A>
<CODE>const</CODE> member functions aren't the only ones that need to worry about returning handles. Even non-<CODE>const</CODE> member functions must reconcile themselves to the fact that the validity of a handle expires at the same time as the object to which it corresponds. This may be sooner than a client expects, especially when the object in question is a compiler-generated temporary <NOBR>object.<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P>
<A NAME="18135"></A>
<P><A NAME="dingp27"></A>
For example, take a look at this function, which returns a <CODE>String</CODE> <NOBR>object:<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>
<A NAME="18124"></A>
<UL><PRE>
String someFamousAuthor() // randomly chooses and
{ // returns an author's name
</PRE>
</UL><A NAME="19321"></A>
<UL><PRE>
switch (rand() % 3) { // rand() is in <stdlib.h>
// (and <cstdlib> — see
// <A HREF="./EC7_FR.HTM#8392" TARGET="_top">Item 49</A>)
case 0:
return "Margaret Mitchell"; // Wrote "Gone with the
// Wind," a true classic
case 1:
return "Stephen King"; // His stories have kept
// millions from sleeping
// at night
case 2:
return "Scott Meyers"; // Ahem, one of these
} // things is not like the
// others...
</PRE>
</UL><A NAME="31793"></A>
<UL><PRE>
return ""; // we can't get here, but
// all paths in a value-
// returning function must
} // return a value, sigh
</PRE>
</UL><A NAME="222239"></A>
<P><A NAME="dingp28"></A>
Kindly set aside your concerns about how "random" the values returned from <CODE>rand</CODE> are, and please humor my delusions of grandeur in associating myself with real writers. Instead, focus on the fact that the return value of <CODE>someFamousAuthor</CODE> is a <CODE>String</CODE> object, a <I>temporary</I> <CODE>String</CODE> object (see <A HREF="../MEC/MC4_FR.HTM#41177" TARGET="_top">Item M19</A>). Such objects are transient — their lifetimes generally extend only until the end of the expression containing the call to the function creating them. In this case, that would be until the end of the expression containing the call to <CODE>someFamousAuthor</CODE>.<SCRIPT>create_link(28);</SCRIPT>
</P>
<A NAME="18160"></A>
<P><A NAME="dingp29"></A>
<A NAME="p128"></A>Now consider this use of <CODE>someFamousAuthor</CODE>, in which we assume that <CODE>String</CODE> declares an <CODE>operator</CODE> <CODE>const</CODE> <CODE>char*</CODE> member function as described <NOBR>above:<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>
<A NAME="18138"></A>
<UL><PRE>const char *pc = someFamousAuthor();
</PRE>
</UL><A NAME="18139"></A>
<UL><PRE>cout << pc; // uh oh...
</PRE>
</UL><A NAME="18140"></A>
<P><A NAME="dingp30"></A>
Believe it or not, you can't predict what this code will do, at least not with any certainty. That's because by the time you try to print out the sequence of characters pointed to by <CODE>pc</CODE>, that sequence is undefined. The difficulty arises from the events that transpire during the initialization of <CODE>pc</CODE>:<SCRIPT>create_link(30);</SCRIPT>
<A NAME="18166"></A><OL TYPE="1"><A NAME="dingp31"></A><LI>A temporary <CODE>String</CODE> object is created to hold <CODE>someFamousAuthor</CODE>'s return value.<SCRIPT>create_link(31);</SCRIPT>
<A NAME="18171"></A><A NAME="dingp32"></A><LI>That <CODE>String</CODE> is converted to a <CODE>const</CODE> <CODE>char*</CODE> via <CODE>String</CODE>'s <CODE>operator</CODE> <CODE>const</CODE> <CODE>char*</CODE> member function, and <CODE>pc</CODE> is initialized with the resulting pointer.<SCRIPT>create_link(32);</SCRIPT>
<A NAME="18172"></A><A NAME="dingp33"></A><LI>The temporary <CODE>String</CODE> object is destroyed, which means its destructor is called. Within the destructor, its <CODE>data</CODE> pointer is deleted (the code is shown in <A HREF="./EC3_FR.HTM#2042" TARGET="_top">Item 11</A>). However, <CODE>data</CODE> points to the same memory as <CODE>pc</CODE> does, so <CODE>pc</CODE> now points to deleted memory — memory with undefined contents.<SCRIPT>create_link(33);</SCRIPT>
</OL></P>
<A NAME="18219"></A>
<P><A NAME="dingp34"></A>
Because <CODE>pc</CODE> was initialized with a handle into a temporary object and temporary objects are destroyed shortly after they're created, the handle became invalid before <CODE>pc</CODE> could do anything with it. For all intents and purposes, <CODE>pc</CODE> was dead on arrival. Such is the danger of handles into temporary <NOBR>objects.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<A NAME="18225"></A>
<P><A NAME="dingp35"></A>
For <CODE>const</CODE> member functions, then, returning handles is ill-advised, because it violates abstraction. Even for non-<CODE>const</CODE> member functions, however, returning handles can lead to trouble, especially when temporary objects get involved. Handles can dangle, just like pointers, and just as you labor to avoid dangling pointers, you should strive to avoid dangling handles, <NOBR>too.<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P>
<A NAME="18987"></A>
<P><A NAME="dingp36"></A>
Still, there's no reason to get fascist about it. It's not possible to stomp out all possible dangling pointers in nontrivial programs, and it's rarely possible to eliminate all possible dangling handles, either. Nevertheless, if you avoid returning handles when there's no compelling need, your programs will benefit, and so will your <NOBR>reputation.<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P>
<!-- SectionName="E30: Avoid ptr/ref returns that violate constness" -->
<A NAME="6599"></A><A NAME="p129"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#6490">Item 29: Avoid returning "handles" to internal data.</A>
<BR>Continue to <A HREF="#6650">Item 31: Never return a reference to a local object or to a dereferenced pointer initialized by new within the function.</A></FONT></DIV>
<P><A NAME="dingp37"></A><FONT ID="eititle">Item 30: Avoid member functions that return non-<CODE>const</CODE> pointers or references to members less accessible than themselves.</FONT><SCRIPT>create_link(37);</SCRIPT>
</P>
<A NAME="6600"></A>
<P><A NAME="dingp38"></A>
The reason for making a member private or protected is to limit access to it, right? Your overworked, underpaid C++ compilers go to lots of trouble to make sure that your access restrictions aren't circumvented, right? So it doesn't make a lot of sense for you to write functions that give random clients the ability to freely access restricted members, now, does it? If you think it <I>does</I> make sense, please reread this paragraph over and over until you agree that it <NOBR>doesn't.<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="6601"></A>
<P><A NAME="dingp39"></A>
It's easy to violate this simple rule. Here's an <NOBR>example:<SCRIPT>create_link(39);</SCRIPT>
</NOBR></P>
<A NAME="6603"></A>
<UL><PRE>class Address { ... }; // where someone lives
</PRE>
</UL><A NAME="6606"></A>
<UL><PRE>class Person {
public:
Address& personAddress() { return address; }
...
</PRE>
</UL><A NAME="18280"></A>
<UL><PRE>private:
Address address;
...
};
</PRE>
</UL><A NAME="6610"></A>
<P><A NAME="dingp40"></A>
The member function <CODE>personAddress</CODE> provides the caller with the <CODE>Address</CODE> object contained in the <CODE>Person</CODE> object, but, probably due to efficiency considerations, the result is returned by reference instead of by value (see <A HREF="./EC4_FR.HTM#6133" TARGET="_top">Item 22</A>). Unfortunately, the presence of this member function defeats the purpose of making <CODE>Person::address</CODE> <NOBR>private:<SCRIPT>create_link(40);</SCRIPT>
</NOBR></P>
<A NAME="6614"></A>
<UL><PRE>
Person scott(...); // parameters omitted for
// simplicity
</PRE>
</UL><A NAME="6615"></A>
<UL><PRE>
Address& addr = // assume that addr is
scott.personAddress(); // global
</PRE>
</UL><A NAME="6616"></A>
<P><A NAME="dingp41"></A>
Now the global object <CODE>addr</CODE> is <I>another name</I> for <CODE>scott.address</CODE>, and it can be used to read and write <CODE>scott.address</CODE> at will. For all practical purposes, <CODE>scott.address</CODE> is no longer private; it is public, and the source of this promotion in accessibility is the member function <CODE>personAddress</CODE>. Of course, there is nothing special about the access level <CODE>private</CODE> in this example; if <CODE>address</CODE> were protected, exactly the same reasoning would <NOBR>apply.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P>
<A NAME="6617"></A>
<P><A NAME="dingp42"></A>
References aren't the only cause for concern. Pointers can play this game, too. Here's the same example, but using pointers this <NOBR>time:<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P>
<A NAME="6619"></A>
<UL><PRE><A NAME="p130"></A>class Person {
public:
Address * personAddress() { return &address; }
...
</PRE>
</UL><A NAME="18291"></A>
<UL><PRE>private:
Address address;
...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -