📄 effective c++ 2e item29.htm
字号:
// 调用B.operator char*()</P>
<P>strcpy(str, "Hi Mom"); // 修改str指向的值</P>
<P>B的值现在还是"Hello World"吗?或者,它是否已经变成了对母亲的问候语?答案完全取决于String::operator
char*的实现。</P>
<P>下面是一个有欠考虑的实现,它导致了错误的结果。但是,它工作起来确实很高效,所以很多程序员才掉进它的错误陷阱之中:</P>
<P>// 一个执行很快但不正确的实现<BR>inline String::operator char*() const<BR>{ return data;
}</P>
<P>这个函数的缺陷在于它返回了一个"句柄"(在本例中,是个指针),而这个句柄所指向的信息本来是应该隐藏在被调用函数所在的String对象的内部。这样,这个句柄就给了调用者自由访问data所指的私有数据的机会。换句话说,有了下面的语句:</P>
<P>char *str = B;</P>
<P>情况就会变成这样:</P>
<P>str------------------------->"Hello
World\0"<BR>
/
<BR>
/<BR>B.data</P>
<P>显然,任何对str所指向的内存的修改都使得B的有效值发生变化。所以,即使B声明为const,而且即使只是调用了B的某个const成员函数,B也会在程序运行过程中得到不同的值。特别是,如果str修改了它所指的值,B也会改变。</P>
<P>String::operator
char*本身写的没有一点错,麻烦的是它可以用于const对象。如果这个函数不声明为const,就不会有问题,因为这样它就不能用于象B这样的const对象了。</P>
<P>但是,将一个String对象转换成它相应的char*形式是很合理的一件事,无论这个对象是否为const。所以,还是应该使函数保持为const。这样的话,就得重写这个函数,使得它不返回指向对象内部数据的句柄:</P>
<P>// 一个执行慢但很安全的实现<BR>inline String::operator char*() const<BR>{<BR> char
*copy = new char[strlen(data) + 1];<BR> strcpy(copy, data);</P>
<P> return copy;</P>
<P>}</P>
<P>这个实现很安全,因为它返回的指针所指向的数据只是String对象所指向数据的拷贝;通过函数返回的指针无法修改String对象的值。当然,安全是要有代价的:这个版本的String::operator
char* 运行起来比前面那个简单版本要慢;此外,函数的调用者还要记得delete掉返回的指针。</P>
<P>如果不能忍受这个版本的速度,或者担心内存泄露,可以来一点小小的改动:使函数返回一个指向const char的指针:</P>
<P>class String {<BR>public:<BR> operator const char *() const;</P>
<P> ...</P>
<P>};</P>
<P>inline String::operator const char*() const<BR>{ return data; }</P>
<P>这个函数既快又安全。虽然它和最初给出的那个函数不一样,但它可以满足大多数程序的需要。这个做法还和C++标准组织处理string/char*难题的方案一致:标准string类型中包含一个成员函数c_str,它的返回值是string的const
char*版本。关于标准string类型的更多信息参见条款49。</P>
<P>指针并不是返回内部数据句柄的唯一途径。引用也很容易被滥用。下面是一种常见的用法,还是拿String类做例子:</P>
<P>class String {<BR>public:</P>
<P> ...</P>
<P> char& operator[](int index) const<BR> { return data[index];
}</P>
<P>private:<BR> char *data;<BR>};</P>
<P>String s = "I'm not constant";</P>
<P>s[0] =
'x';
// 正确, s不是const</P>
<P>const String cs = "I'm constant";</P>
<P>cs[0] =
'x';
// 修改了const
string,<BR>
// 但编译器不会通知</P>
<P>注意String::operator[]是通过引用返回结果的。这意味着函数的调用者得到的是内部数据data[index]的另一个名字,而这个名字可以用来修改const对象的内部数据。这个问题和前面看到的相同,只不过这次的罪魁祸首是引用,而不是指针。</P>
<P>这类问题的通用解决方案和前面关于指针的讨论一样:或者使函数为非const,或者重写函数,使之不返回句柄。如果想让String::operator[]既适用于const对象又适用于非const
对象,可以参见条款21。</P>
<P>并不是只有const成员函数需要担心返回句柄的问题,即使是非const成员函数也得承认:句柄的合法性失效的时间和它所对应的对象是完全相同的。这个时间可能比用户期望的要早很多,特别是当涉及的对象是由编译器产生的临时对象时。</P>
<P>例如,看看这个函数,它返回了一个String对象:</P>
<P>String
someFamousAuthor()
//
随机选择一个作家名<BR>{
// 并返回之</P>
<P> switch (rand() % 3)
{ //
rand()在<stdlib.h>中<BR>
// (还有<cstdlib>。参见条款49)<BR> case 0:<BR> return
"Margaret Mitchell"; // 此作家曾写了
"飘",<BR>
// 一部绝对经典的作品<BR> case 1:<BR> return "Stephen
King"; //
他的小说使得许多人<BR>
// 彻夜不眠<BR> case 2:<BR> return "Scott
Meyers"; //
嗯...滥竽充数的一个<BR>
}
<BR>
</P>
<P> return
"";
//
程序不会执行到这儿,<BR>
//
但对于一个有返回值的函数来说,<BR>
// 任何执行途径上都要有返回值<BR>}</P>
<P>希望你的注意力不要集中在随机数是怎样从rand产生的问题上,也不要嘲笑我把自己和这些作家联系在一起。真正要注意的是,someFamousAuthor的返回值是一个String对象,一个临时String对象(参见条款M19)。这样的对象是暂时性的,它们的生命周期通常在函数调用表达式结束时终止。例如上面的情况中,包含someFamousAuthor函数调用的表达式结束时,返回值对象的生命周期也就随之结束。</P>
<P>具体看看下面这个使用someFamousAuthor的例子,假设String声明了一个上面的operator const char*成员函数:</P>
<P>const char *pc = someFamousAuthor();</P>
<P>cout <<
pc;
</P>
<P>不论你是否相信,谁也不能预测这段代码将会做些什么,至少不能确定它会做些什么。因为当你想打印pc所指的字符串时,字符串的值是不确定的。造成这一结果的原因在于pc初始化时发生了下面这些事件:<BR>1.
产生一个临时String对象用以保存someFamousAuthor的返回值。<BR>2. 通过String的operator const
char*成员函数将临时String对象转换为const char*指针,并用这个指针初始化pc。<BR>3.
临时String对象被销毁,其析构函数被调用。析构函数中,data指针被删除(代码详见条款11)。然而,data和pc所指的是同一块内存,所以现在pc指向的是被删除的内存--------其内容是不可确定的。</P>
<P>因为pc是被一个指向临时对象的句柄初始化的,而临时对象在被创建后又立即被销毁,所以在pc被使用前句柄已经是非法的了。也就是说,无论想做什么,当要使用pc时,pc其实已经名存实亡。这就是指向临时对象的句柄所带来的危害。</P>
<P>所以,对于const成员函数来说,返回句柄是不明智的,因为它会破坏数据抽象。对于非const成员函数来说,返回句柄会带来麻烦,特别是涉及到临时对象时。句柄就象指针一样,可以是悬浮(dangle)的。所以一定要象避免悬浮的指针那样,尽量避免悬浮的句柄。</P>
<P>同样不能对本条款绝对化。在一个大的程序中想消灭所有可能的悬浮指针是不现实的,想消灭所有可能的悬浮句柄也是不现实的。但是,只要不是万不得已,就要避免返回句柄,这样,不但程序会受益,用户也会更信赖你。</P><BR><BR></DIV></DIV></DIV><BR><BR>
<SCRIPT src="Effective C++ 2e Item29.files/get_readnum.htm"></SCRIPT>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#990000 id=white><FONT
color=#ffffff>对该文的评论</FONT></TH></TR></TBODY></TABLE><BR>
<SCRIPT language=javascript>
<!--
function isEmpty(s)
{
return ((s == null) || (s.length == 0))
}
function submit1()
{
if (isEmpty(document.add_critique.csdnname.value) || isEmpty(document.add_critique.csdnpassword.value) || isEmpty(document.add_critique.critique_content.value))
{
alert('登陆名,密码,评论不能为空!!!!') ;
return false;
}
document.add_critique.submit();
}
//-->
</SCRIPT>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#990000 id=white><FONT
color=#ffffff>发表评论</FONT></TH></TR></TBODY></TABLE>
<TABLE align=center bgColor=#ffffff border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TD>
<FORM action=/develop/add_critique.asp method=post
name=add_critique><INPUT name=critique_add type=hidden value=add> <INPUT
name=from type=hidden value=8991> 评论人: <INPUT name=csdnname>
密码: <INPUT name=csdnpassword type=password>
评论:<BR> <TEXTAREA cols=100 name=critique_content rows=8></TEXTAREA><BR>
<INPUT name=ubmit onclick=javascript:submit1(); type=button value=发表评论>
<INPUT name=id type=hidden value=8991> </FORM></TD></TR></TBODY></TABLE>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>
<TABLE border=0 cellPadding=2 width=770>
<TBODY>
<TR>
<TD height=10></TD></TR>
<TR>
<TD align=left width=130><A
href="http://www.csdn.net/news/looknews.asp?id=2313"
target=_blank><IMG border=0
src="http://www.csdn.net/job/images/csdn_job.gif"></A> </TD>
<TD align=middle width=510>
<OBJECT classid=clsid:D27CDB6E-AE6D-11cf-96B8-444553540000
codeBase=http://active.macromedia.com/flash2/cabs/swflash.cab#version=4,0,0,0
height=60 id=Movie1 width=468><PARAM NAME="movie" VALUE="http://www.csdn.net/images/ad/csdn_media.swf"><PARAM NAME="quality" VALUE="high">
<EMBED src="http://www.csdn.net/images/ad/csdn_media.swf"
quality=high WIDTH=468 HEIGHT=60
TYPE="application/x-shockwave-flash"
PLUGINSPAGE="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash">
</EMBED> </OBJECT></TD>
<TD align=middle width=130><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 src="http://www.csdn.net/images/biaoshi.gif"></A>
</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD>
<TABLE align=default bgColor=#1f60a0 border=0 cellPadding=0 cellSpacing=0
height=20 width=770>
<TBODY>
<TR align=middle class=font13px vAlign=center>
<TD class=font13px height=2 vAlign=center width=90></TD>
<TD rowSpan=2 width=2><IMG height=13
src="http://www.csdn.net/images/ad4.gif" width=2></TD>
<TD class=font13px height=2 width=90></TD>
<TD rowSpan=2 width=3><FONT color=#ffffff><IMG height=13
src="http://www.csdn.net/images/ad4.gif" width=2></FONT></TD>
<TD class=font13px height=2 width=90></TD>
<TD rowSpan=2 width=2><IMG height=13
src="http://www.csdn.net/images/ad4.gif" width=2></TD>
<TD class=font13px height=2 width=90></TD>
<TD rowSpan=2 width=3><FONT color=#ffffff><IMG height=13
src="http://www.csdn.net/images/ad4.gif" width=2></FONT></TD>
<TD height=2 width=350><FONT color=#ffffff></FONT></TD>
<TD rowSpan=2 width=10><FONT color=#ffffff><IMG height=11
src="http://www.csdn.net/images/ad3.gif" width=6></FONT></TD>
<TD class=font13px height=2 width=60></TD></TR>
<TR align=middle class=font14px vAlign=center>
<TD class=font13px vAlign=center width=90><A
href="http://www.csdn.net/intro/intro.shtm"><FONT
color=#ffffff>美达美简介</FONT></A></TD>
<TD class=font13px width=90><A
href="http://www.csdn.net/intro/ad.shtm"><FONT
color=#ffffff>广告服务</FONT></A></TD>
<TD class=font13px width=90><A
href="http://www.csdn.net/English/"><FONT
color=#ffffff>英语步步高</FONT></A></TD>
<TD class=font13px width=90><A href="http://www.csdn.net/dev/"><FONT
color=#ffffff>程序员大本营</FONT></A></TD>
<TD align=right width=350><SPAN class=font13px><A
href="mailto:webmaster@csdn.net"><FONT
color=#ffffff>百联美达美科技有限公司</FONT></A></SPAN><FONT color=#ffffff><SPAN
class=font13px> </SPAN></FONT></TD>
<TD class=font13px width=60><FONT color=#ffffff>版权所有</FONT>
<SCRIPT>document.write("<img src=http://202.106.156.10/stat.asp?user=designol&refer="+escape(document.referrer)+"&cur="+escape(document.URL)+" width=0 height=0 border=0>");</SCRIPT>
</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -