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

📄 mi7.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 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>More Effective C++ | Item 7: Never overload &&, ||, or ,</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var dingbase = "MI7_DIR.HTM";
var dingtext = "Item M7, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName = "M7: Never overload &&, ||, or ," -->
<A NAME="77702"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI6_FR.HTM" TARGET="_top">Item 6: Distinguish between prefix and postfix forms of increment and decrement operators</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI8_FR.HTM" TARGET="_top">Item 8: Understand the different meanings of new and delete</A></FONT></DIV>

<A NAME="p35"></A>
<P><A NAME="dingp1"></A><font ID="mititle">Item 7: &nbsp;Never overload <CODE>&amp;&amp;</CODE>, <CODE>||</CODE>, or <CODE>,</CODE>.</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="6796"></A>
<A NAME="6798"></A>
<P><A NAME="dingp2"></A>
Like C, C++ employs short-circuit evaluation of boolean expressions. This means that once the truth or falsehood of an expression has been determined, evaluation of the expression ceases, even if some parts of the expression haven't yet been examined. For example, in this <NOBR>case,<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="83231"></A>
<UL><PRE>char *p;
</PRE>
</UL><A NAME="77706"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="77707"></A>
<UL><PRE>if ((p != 0) &amp;&amp; (strlen(p) &gt; 10)) ...
</PRE>
</UL><A NAME="77708"></A><P><A NAME="dingp3"></A>there is no need to worry about invoking <CODE>strlen</CODE> on <CODE>p</CODE> if it's a null pointer, because if the test of <CODE>p</CODE> against 0 fails, <CODE>strlen</CODE> will never be called. Similarly, <NOBR>given<SCRIPT>create_link(3);</SCRIPT>
</NOBR></p><A NAME="77709"></A>
<UL><PRE>int rangeCheck(int index)
{
 if ((index &lt; lowerBound) || (index &gt; upperBound)) ...
<A NAME="77710"></A> ...
<A NAME="77711"></A>}
</PRE>
</UL><A NAME="77712"></A>
<P><A NAME="dingp4"></A>
<CODE>index</CODE> will never be compared to <CODE>upperBound</CODE> if it's less than <CODE>lowerBound</CODE>.<SCRIPT>create_link(4);</SCRIPT>
</p><A NAME="77713"></A>
<P><A NAME="dingp5"></A>
This is the behavior that has been drummed into C and C++ programmers since time immemorial, so this is what they expect. Furthermore, they write programs whose correct behavior <I>depends</I> on short-circuit evaluation. In the first code fragment above, for example, it is important that <CODE>strlen</CODE> not be invoked if <CODE>p</CODE> is a null pointer, because the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver="self.status='ISO/ANSI standard for C++'; return true" onMouseOut="self.status=self.defaultStatus" target="_top">standard</NOBR> for C++</A> states (as does the standard for C) that the result of invoking <CODE>strlen</CODE> on a null pointer is <NOBR>undefined.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></p><A NAME="77714"></A>
<P><A NAME="dingp6"></A>
C++ allows you to customize the behavior of the <CODE>&amp;&amp;</CODE> and <CODE>||</CODE> operators for user-defined types. You do it by overloading the functions <CODE>operator&amp;&amp;</CODE> and <CODE>operator||</CODE>, and you can do this at the global scope or on a per-class basis. If you decide to take advantage of this opportunity, however, you must be aware that you are changing the rules of the game quite radically, because you are replacing short-circuit semantics with <I>function call</I> semantics. That is, if you overload <CODE>operator&amp;&amp;</CODE>, what looks to you like <NOBR>this,<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>

<A NAME="77715"></A>
<UL><PRE>if (expression1 &amp;&amp; expression2) ...
</PRE>
</UL><A NAME="77716"></A><P><A NAME="dingp7"></A>looks to compilers like one of <NOBR>these:<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="77717"></A>
<UL><PRE>if (expression1.operator&amp;&amp;(expression2)) ...
                              // when operator&amp;&amp; is a
                              // member function
</PRE>
</UL><A NAME="77718"></A>
<UL><PRE><A NAME="p36"></A>if (operator&amp;&amp;(expression1, expression2)) ...
                              // when operator&amp;&amp; is a
                              // global function
</PRE>
</UL><A NAME="6413"></A>

<P><A NAME="dingp8"></A>
This may not seem like that big a deal, but function call semantics differ from short-circuit semantics in two crucial ways. First, when a function call is made, <I>all</I> parameters must be evaluated, so when calling the functions <CODE>operator&amp;&amp;</CODE> and <CODE>operator||</CODE>, <I>both</I> parameters are evaluated. There is, in other words, no short circuit. Second, the language specification leaves undefined the order of evaluation of parameters to a function call, so there is no way of knowing whether <CODE>expression1</CODE> or <CODE>expression2</CODE> will be evaluated first. This stands in stark contrast to short-circuit evaluation, which <I>always</I> evaluates its arguments in left-to-right <NOBR>order.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></p><A NAME="6414"></A>
<P><A NAME="dingp9"></A>
As a result, if you overload <CODE>&amp;&amp;</CODE> or <CODE>||</CODE>, there is no way to offer programmers the behavior they both expect and have come to depend on. So don't overload <CODE>&amp;&amp;</CODE> or <CODE>||</CODE>.<SCRIPT>create_link(9);</SCRIPT>
</p><A NAME="77721"></A>
<P><A NAME="dingp10"></A>
The situation with the comma operator is similar, but before we delve into that, I'll pause and let you catch the breath you lost when you gasped, "The comma operator? There's a <I>comma</I> operator?" There is <NOBR>indeed.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></p><A NAME="89934"></A>
<P><A NAME="dingp11"></A>
The comma operator is used to form <I>expressions</I>, and you're most likely to run across it in the update part of a <CODE>for</CODE> loop. The following function, for example, is based on one in the second edition of Kernighan's and Ritchie's classic <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=c2pl" onMouseOver = "self.status = 'The C Programming Language Home Page'; return true" onMouseOut = "self.status = self.defaultStatus" target="_top"><i>The</NOBR> C Programming Language</i></A> (Prentice-Hall, <NOBR>1988):<SCRIPT>create_link(11);</SCRIPT>
</NOBR></p><A NAME="77723"></A>
<UL><PRE>// reverse string s in place
void reverse(char s[])
<A NAME="77724"></A>{
 for (int i = 0, j = strlen(s)-1;
     i &lt; j;
     ++i, --j)         // aha! the comma operator!
 {
  int c = s[i];
  s[i] = s[j];
  s[j] = c;
 }
}
</PRE>
</UL><A NAME="77725"></A>
<P><A NAME="dingp12"></A>
Here, <CODE>i</CODE> is incremented and <CODE>j</CODE> is decremented in the final part of the <CODE>for</CODE> loop. It is convenient to use the comma operator here, because only an expression is valid in the final part of a <CODE>for</CODE> loop; separate statements to change the values of <CODE>i</CODE> and <CODE>j</CODE> would be <NOBR>illegal.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></p><A NAME="77726"></A>
<A NAME="p37"></A><P><A NAME="dingp13"></A>
Just as there are rules in C++ defining how <CODE>&amp;&amp;</CODE> and <CODE>||</CODE> behave for built-in types, there are rules defining how the comma operator behaves for such types. An expression containing a comma is evaluated by first evaluating the part of the expression to the left of the comma, then evaluating the expression to the right of the comma; the result of the overall comma expression is the value of the expression on the right. So in the final part of the loop above, compilers first evaluate <CODE>++i</CODE>, then <CODE>--j</CODE>, and the result of the comma expression is the value returned from <CODE>--j</CODE>.<SCRIPT>create_link(13);</SCRIPT>
</p><A NAME="77727"></A>
<P><A NAME="dingp14"></A>
Perhaps you're wondering why you need to know this. You need to know because you need to mimic this behavior if you're going to take it upon yourself to write your own comma operator. Unfortunately, you can't perform the requisite <NOBR>mimicry.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></p><A NAME="77728"></A>
<P><A NAME="dingp15"></A>
If you write <CODE>operator,</CODE> as a non-member function, you'll never be able to guarantee that the left-hand expression is evaluated before the right-hand expression, because both expressions will be passed as arguments in a function call (to <CODE>operator,</CODE>). But you have no control over the order in which a function's arguments are evaluated. So the non-member approach is definitely <NOBR>out.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></p><A NAME="77729"></A>
<P><A NAME="dingp16"></A>
That leaves only the possibility of writing <CODE>operator,</CODE> as a member function. Even here you can't rely on the left-hand operand to the comma operator being evaluated first, because compilers are not constrained to do things that way. Hence, you can't overload the comma operator and also guarantee it will behave the way it's supposed to. It therefore seems imprudent to overload it at <NOBR>all.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></p><A NAME="77730"></A>
<P><A NAME="dingp17"></A>
You may be wondering if there's an end to this overloading madness. After all, if you can overload the comma operator, what <I>can't</I> you overload? As it turns out, there are limits. You can't overload the following <NOBR>operators:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></p><A NAME="77731"></A>
<UL><PRE>
.              .*              ::             ?:<A NAME="77757"></A>
new          delete        sizeof      typeid<A NAME="77756"></A>
static_cast  dynamic_cast  const_cast  reinterpret_cast
</PRE></UL>
<A NAME="77766"></A>
<A NAME="dingp18"></A>You can overload these:<SCRIPT>create_link(18);</SCRIPT>

<A NAME="77767"></A>
<UL><PRE>
operator new        operator delete<A NAME="77771"></A>
operator   <NOBR>new[]</NOBR>    operator <NOBR>delete[]</NOBR><A NAME="77768"></A>
+    -   *   /   %   ^     &amp;   |     ~<A NAME="77743"></A>
!    =   &lt;   &gt;  +=   -=   *=   /=   %=<A NAME="77744"></A>
^=  &amp;=  |=  &lt;&lt;  &gt;&gt;   &gt;&gt;=  &lt;&lt;=  ==   !=<A NAME="77745"></A>
&lt;=  &gt;=  &amp;&amp;  ||  ++   --    ,   -&gt;*  -&gt;<A NAME="77746"></A>
()  []
</PRE>
</UL><A NAME="77776"></A>
<A NAME="p38"></A><P><A NAME="dingp19"></A>
(For information on the <CODE>new</CODE> and <CODE>delete</CODE> operators, as well as <CODE>operator</CODE> <CODE>new</CODE>, <CODE>operator</CODE> <CODE>delete</CODE>, <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE>, and <CODE>operator</CODE> <CODE><NOBR>delete[]</NOBR></CODE>, see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>.)<SCRIPT>create_link(19);</SCRIPT>
</p><A NAME="77747"></A>
<P><A NAME="dingp20"></A>
Of course, just because you can overload these operators is no reason to run off and do it. The purpose of operator overloading is to make programs easier to read, write, and understand, not to dazzle others with your knowledge that comma is an operator. If you don't have a good reason for overloading an operator, don't overload it. In the case of <CODE>&amp;&amp;</CODE>, <CODE>||</CODE>, and <CODE>,</CODE>, it's difficult to have a good reason, because no matter how hard you try, you can't make them behave the way they're supposed <NOBR>to.<SCRIPT>create_link(20);</SCRIPT>
</NOBR></p>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI6_FR.HTM" TARGET="_top">Item 6: Distinguish between prefix and postfix forms of increment and decrement operators</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI8_FR.HTM" TARGET="_top">Item 8: Understand the different meanings of new and delete</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -