⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 effective c++ 2e item29.htm

📁 Effective-c++.国外很经典的一本关于c++编程的电子书。
💻 HTM
📖 第 1 页 / 共 2 页
字号:
// 调用B.operator char*()</P>
<P>strcpy(str, "Hi Mom");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 修改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-------------------------&gt;"Hello 
World\0"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
/&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
/<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>&nbsp; char 
*copy = new char[strlen(data) + 1];<BR>&nbsp; strcpy(copy, data);</P>
<P>&nbsp; return copy;</P>
<P>}</P>
<P>这个实现很安全,因为它返回的指针所指向的数据只是String对象所指向数据的拷贝;通过函数返回的指针无法修改String对象的值。当然,安全是要有代价的:这个版本的String::operator 
char* 运行起来比前面那个简单版本要慢;此外,函数的调用者还要记得delete掉返回的指针。</P>
<P>如果不能忍受这个版本的速度,或者担心内存泄露,可以来一点小小的改动:使函数返回一个指向const char的指针:</P>
<P>class String {<BR>public:<BR>&nbsp; operator const char *() const;</P>
<P>&nbsp; ...</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>&nbsp; ...</P>
<P>&nbsp; char&amp; operator[](int index) const<BR>&nbsp; { return data[index]; 
}</P>
<P>private:<BR>&nbsp; char *data;<BR>};</P>
<P>String s = "I'm not constant";</P>
<P>s[0] = 
'x';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 正确, s不是const</P>
<P>const String cs = "I'm constant";</P>
<P>cs[0] = 
'x';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 修改了const 
string,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 但编译器不会通知</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()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 
随机选择一个作家名<BR>{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 并返回之</P>
<P>&nbsp; switch (rand() % 3) 
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 
rand()在&lt;stdlib.h&gt;中<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// (还有&lt;cstdlib&gt;。参见条款49)<BR>&nbsp; case 0:<BR>&nbsp;&nbsp;&nbsp; return 
"Margaret Mitchell";&nbsp;&nbsp;&nbsp;&nbsp; // 此作家曾写了 
"飘",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 一部绝对经典的作品<BR>&nbsp; case 1:<BR>&nbsp;&nbsp;&nbsp; return "Stephen 
King";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 
他的小说使得许多人<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 彻夜不眠<BR>&nbsp; case 2:<BR>&nbsp;&nbsp;&nbsp; return "Scott 
Meyers";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 
嗯...滥竽充数的一个<BR>&nbsp; 
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
</P>
<P>&nbsp; return 
"";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 
程序不会执行到这儿,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 
但对于一个有返回值的函数来说,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
// 任何执行途径上都要有返回值<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 &lt;&lt; 
pc;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
</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> &nbsp;&nbsp;评论人: <INPUT name=csdnname> 
      &nbsp;&nbsp;密码: <INPUT name=csdnpassword type=password> 
      &nbsp;&nbsp;评论:<BR>&nbsp;&nbsp; <TEXTAREA cols=100 name=critique_content rows=8></TEXTAREA><BR>&nbsp;&nbsp; 
<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 + -