📄 mc2.htm
字号:
<UL><PRE>if ((p != 0) && (strlen(p) > 10)) ...
</PRE>
</UL><A NAME="77708"></A><P><A NAME="dingp57"></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(57);</SCRIPT>
</NOBR></p><A NAME="77709"></A>
<UL><PRE>int rangeCheck(int index)
{
if ((index < lowerBound) || (index > upperBound)) ...<A NAME="77710"></A>
...<A NAME="77711"></A>
}
</PRE>
</UL><A NAME="77712"></A>
<P><A NAME="dingp58"></A>
<CODE>index</CODE> will never be compared to <CODE>upperBound</CODE> if it's less than <CODE>lowerBound</CODE>.<SCRIPT>create_link(58);</SCRIPT>
</p><A NAME="77713"></A>
<P><A NAME="dingp59"></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>°</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(59);</SCRIPT>
</NOBR></p><A NAME="77714"></A>
<P><A NAME="dingp60"></A>
C++ allows you to customize the behavior of the <CODE>&&</CODE> and <CODE>||</CODE> operators for user-defined types. You do it by overloading the functions <CODE>operator&&</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&&</CODE>, what looks to you like <NOBR>this,<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P>
<A NAME="77715"></A>
<UL><PRE>if (expression1 && expression2) ...
</PRE>
</UL><A NAME="77716"></A><P><A NAME="dingp61"></A>looks to compilers like one of <NOBR>these:<SCRIPT>create_link(61);</SCRIPT>
</NOBR></P>
<A NAME="77717"></A>
<UL><PRE>if (expression1.operator&&(expression2)) ...
// when operator&& is a
// member function
</PRE>
</UL><A NAME="77718"></A>
<UL><PRE><A NAME="p36"></A>if (operator&&(expression1, expression2)) ...
// when operator&& is a
// global function
</PRE>
</UL><A NAME="6413"></A>
<P><A NAME="dingp62"></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&&</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(62);</SCRIPT>
</NOBR></p><A NAME="6414"></A>
<P><A NAME="dingp63"></A>
As a result, if you overload <CODE>&&</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>&&</CODE> or <CODE>||</CODE>.<SCRIPT>create_link(63);</SCRIPT>
</p><A NAME="77721"></A>
<P><A NAME="dingp64"></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(64);</SCRIPT>
</NOBR></p><A NAME="89934"></A>
<P><A NAME="dingp65"></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>°</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(65);</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 < 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="dingp66"></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(66);</SCRIPT>
</NOBR></p><A NAME="77726"></A>
<A NAME="p37"></A><P><A NAME="dingp67"></A>
Just as there are rules in C++ defining how <CODE>&&</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(67);</SCRIPT>
</p><A NAME="77727"></A>
<P><A NAME="dingp68"></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(68);</SCRIPT>
</NOBR></p><A NAME="77728"></A>
<P><A NAME="dingp69"></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(69);</SCRIPT>
</NOBR></p><A NAME="77729"></A>
<P><A NAME="dingp70"></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(70);</SCRIPT>
</NOBR></p><A NAME="77730"></A>
<P><A NAME="dingp71"></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(71);</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></NOBR></P>
<A NAME="77767"></A><A NAME="dingp72"></A>You can overload these:<SCRIPT>create_link(72);</SCRIPT>
<UL><PRE>
operator new operator delete<A NAME="77771"></A>
operator <NOBR>new[]</NOBR> operator <NOBR>delete[]</NOBR><A NAME="77768"></A>
+ - * / % ^ & | ~<A NAME="77743"></A>
! = < > += -= *= /= %=<A NAME="77744"></A>
^= &= |= << >> >>= <<= == !=<A NAME="77745"></A>
<= >= && || ++ -- , ->* -><A NAME="77746"></A>
() []
</PRE>
</UL><A NAME="77776"></A>
<A NAME="p38"></A><P><A NAME="dingp73"></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="#33985" onMouseOver = "self.status = 'Link to MEC++ Item 8'; return true" onMouseOut = "self.status = self.defaultStatus">Item 8</A>.)<SCRIPT>create_link(73);</SCRIPT>
</p><A NAME="77747"></A>
<P><A NAME="dingp74"></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>&&</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(74);</SCRIPT>
</NOBR></p>
<!-- SectionName="MEC++ Item 8: The different meanings of new and delete" -->
<A NAME="33985"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC2.HTM#77702">Item 7: Never overload &&, ||, or ,</A> <BR> Continue to <A HREF="./MC3.HTM#71622" TARGET="_top">Exceptions</A></FONT></DIV>
<P><A NAME="dingp75"></A><font ID="mititle">Item 8: Understand the different meanings of <CODE>new</CODE>
and <CODE>delete</CODE>.</font><SCRIPT>create_link(75);</SCRIPT>
</P>
<A NAME="72115"></A>
<A NAME="34122"></A>
<P><A NAME="dingp76"></A>
It occasionally seems as if people went out of their way to make C++ terminology difficult to understand. Case in point: the difference between the <CODE><I>new</I></CODE> <I>operator</I> and <CODE><I>operator</I></CODE> <CODE><I>new</I></CODE>.<SCRIPT>create_link(76);</SCRIPT>
</p><A NAME="34062"></A>
<P><A NAME="dingp77"></A>
When you write code like <NOBR>this,<SCRIPT>create_link(77);</SCRIPT>
</NOBR></P>
<A NAME="34094"></A>
<UL><PRE>string *ps = new string("Memory Management");
</PRE>
</UL><A NAME="34095"></A><P><A NAME="dingp78"></A>
the <CODE>new</CODE> you are using is the <CODE>new</CODE> operator. This operator is built into the language and, like <CODE>sizeof</CODE>, you can't change its meaning: it always does the same thing. What it does is twofold. First, it allocates enough memory to hold an object of the type requested. In the example above, it allocates enough memory to hold a <CODE>string</CODE> object. Second, it calls a constructor to initialize an object in the memory that was allocated. The <CODE>new</CODE> operator always does those two things; you can't change its behavior in any <NOBR>way.<SCRIPT>create_link(78);</SCRIPT>
</NOBR></p><A NAME="34137"></A>
<P><A NAME="dingp79"></A>
What you can change is <I>how</I> the memory for an object is allocated. The <CODE>new</CODE> operator calls a function to perform the requisite memory allocation, and you can rewrite or overload that function to change its behavior. The name of the function the <CODE>new</CODE> operator calls to allocate memory is <CODE>operator</CODE> <CODE>new</CODE>. <NOBR>Honest.<SCRIPT>create_link(79);</SCRIPT>
</NOBR></p><A NAME="34175"></A>
<P><A NAME="dingp80"></A>
The <CODE>operator</CODE> <CODE>new</CODE> function is usually declared like <NOBR>this:<SCRIPT>create_link(80);</SCRIPT>
</NOBR></p><A NAME="34176"></A>
<UL><PRE>void * operator new(size_t size);
</PRE>
</UL><A NAME="11461"></A>
<P><A NAME="dingp81"></A>
The return type is <CODE>void*</CODE>, because this function returns a pointer to raw, uninitialized memory. (If you like, you can write a version of <CODE>operator</CODE> <CODE>new</CODE> that initializes the memory to some value before returning a pointer to it, but this is not commonly done.) The <CODE>size_t</CODE> parameter specifies how much memory to allocate. You can overload <CODE>operator</CODE> <A NAME="p39"></A><CODE>new</CODE> by adding additional parameters, but the first parameter must always be of type <CODE>size_t</CODE>. (For information on writing <CODE>operator</CODE> <CODE>new</CODE>, consult Items <A HREF="../EC/EC2_FR.HTM#120851" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 8'; return true" onMouseOut = "self.status = self.defaultStatus">E8</A>-<A HREF="../EC/EC2_FR.HTM#1986" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 10'; return true" onMouseOut = "self.status = self.defaultStatus">E10</A>.)<SCRIPT>create_link(81);</SCRIPT>
</p><A NAME="11467"></A>
<P><A NAME="dingp82"></A>
You'll probably never want to call <CODE>operator</CODE> <CODE>new</CODE> directly, but on the off chance you do, you'll call it just like any other <NOBR>function:<SCRIPT>create_link(82);</SCRIPT>
</NOBR></p><A NAME="64753"></A>
<UL><PRE>void *rawMemory = operator new(sizeof(string));
</PRE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -