📄 mc2.htm
字号:
</NOBR></P> <A NAME="79176"></A>
<P><A NAME="dingp37"></A>
Before you turn to proxy classes, however, reflect a bit on the lessons of this Item. Granting compilers license to perform implicit type conversions usually leads to more harm than good, so don't provide conversion functions unless you're <I>sure</I> you want <NOBR>them.<SCRIPT>create_link(37);</SCRIPT>
</NOBR></p>
<!-- SectionName="M6: Prefix vs. postfix increment and decrement" -->
<A NAME="5262"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC2.HTM#5970">Item 5: Be wary of user-defined conversion functions</A> <BR> Continue to <A HREF="./MC2.HTM#77702">Item 7: Never overload &&, ||, or ,</A></FONT></DIV>
<P><A NAME="dingp38"></A><font ID="mititle">Item 6: Distinguish between prefix and postfix forms of increment and decrement operators.</font><SCRIPT>create_link(38);</SCRIPT>
</P>
<A NAME="72114"></A>
<A NAME="6889"></A>
<P><A NAME="dingp39"></A>
Long, long ago (the late '80s) in a language far, far away (C++ at that time), there was no way to distinguish between prefix and postfix invocations of the <CODE>++</CODE> and <CODE>--</CODE> operators. Programmers being programmers, <A NAME="p32"></A>they kvetched about this omission, and C++ was extended to allow overloading both forms of increment and decrement <NOBR>operators.<SCRIPT>create_link(39);</SCRIPT>
</NOBR></p><A NAME="6899"></A>
<P><A NAME="dingp40"></A>
There was a syntactic problem, however, and that was that overloaded functions are differentiated on the basis of the parameter types they take, but neither prefix nor postfix increment or decrement takes an argument. To surmount this linguistic pothole, it was decreed that postfix forms take an <CODE>int</CODE> argument, and compilers silently pass 0 as that <CODE>int</CODE> when those functions are <NOBR>called:<SCRIPT>create_link(40);</SCRIPT>
</NOBR></p><A NAME="6900"></A>
<UL><PRE>class UPInt { // "unlimited precision int"
public:
UPInt& operator++(); // prefix ++
const UPInt operator++(int); // postfix ++
<A NAME="6901"></A>
UPInt& operator--(); // prefix --
const UPInt operator--(int); // postfix --
<A NAME="6984"></A>
UPInt& operator+=(int); // a += operator for UPInts
// and ints
...
</PRE>
</UL><A NAME="6903"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="6904"></A>
<UL><PRE>UPInt i;
</PRE>
</UL><A NAME="6923"></A>
<UL><PRE>++i; // calls i.operator++();
i++; // calls i.operator++(0);
</PRE>
</UL><A NAME="6924"></A>
<UL><PRE>--i; // calls i.operator--();
i--; // calls i.operator--(0);
</PRE>
</UL><A NAME="6934"></A>
<P><A NAME="dingp41"></A>
This convention is a little on the odd side, but you'll get used to it. More important to get used to, however, is this: the prefix and postfix forms of these operators return <I>different types</I>. In particular, prefix forms return a reference, postfix forms return a <CODE>const</CODE> object. We'll focus here on the prefix and postfix <code>++</code> operators, but the story for the <code>--</code> operators is <NOBR>analogous.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></p><A NAME="6947"></A>
<P><A NAME="dingp42"></A>
From your days as a C programmer, you may recall that the prefix form of the increment operator is sometimes called "increment and fetch," while the postfix form is often known as "fetch and increment." These two phrases are important to remember, because they all but act as formal specifications for how prefix and postfix increment should be <NOBR>implemented:<SCRIPT>create_link(42);</SCRIPT>
</NOBR></p>
<A NAME="6964"></A>
<UL><PRE>// prefix form: increment and fetch
UPInt& UPInt::operator++()
{
*this += 1; // increment
return *this; // fetch
}
</PRE>
</UL><A NAME="6974"></A>
<UL><PRE><A NAME="p33"></A>// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // fetch
++(*this); // increment
</PRE>
</UL><A NAME="77623"></A>
<UL><PRE>return oldValue; // return what was
} // fetched
</PRE>
</UL><A NAME="7118"></A>
<P><A NAME="dingp43"></A>
Note how the postfix operator makes no use of its parameter. This is typical. The only purpose of the parameter is to distinguish prefix from postfix function invocation. Many compilers issue warnings (see <A HREF="../EC/EC7_FR.HTM#8378" TARGET="_top" onMouseOver = "self.status = 'Link to Link to EC++ Item 48'; return true" onMouseOut = "self.status = self.defaultStatus">Item E48</A>) if you fail to use named parameters in the body of the function to which they apply, and this can be annoying. To avoid such warnings, a common strategy is to omit names for parameters you don't plan to use; that's what's been done <NOBR>above.<SCRIPT>create_link(43);</SCRIPT>
</NOBR></p><A NAME="77639"></A>
<P><A NAME="dingp44"></A>
It's clear why postfix increment must return an object (it's returning an old value), but why a <CODE>const</CODE> object? Imagine that it did not. Then the following would be <NOBR>legal:<SCRIPT>create_link(44);</SCRIPT>
</NOBR></p><A NAME="77642"></A>
<UL><PRE>UPInt i;
</PRE>
</UL><A NAME="77643"></A>
<UL><PRE>i++++; // apply postfix increment
// <I>twice</I>
</PRE>
</UL><A NAME="77654"></A>
<P><A NAME="dingp45"></A>
This is the same <NOBR>as<SCRIPT>create_link(45);</SCRIPT>
</NOBR></P>
<A NAME="77655"></A>
<UL><PRE>i.operator++(0).operator++(0);
</PRE>
</UL><A NAME="77656"></A><P><A NAME="dingp46"></A>
and it should be clear that the second invocation of <CODE>operator++</CODE> is being applied to the object returned from the first <NOBR>invocation.<SCRIPT>create_link(46);</SCRIPT>
</NOBR></p><A NAME="77628"></A>
<P><A NAME="dingp47"></A>
There are two reasons to abhor this. First, it's inconsistent with the behavior of the built-in types. A good rule to follow when designing classes is <I>when in doubt, do as the <CODE>int</CODE>s do</I>, and the <CODE>int</CODE>s most certainly do not allow double application of postfix <NOBR>increment:<SCRIPT>create_link(47);</SCRIPT>
</NOBR></p><A NAME="77648"></A>
<UL><PRE>int i;
</PRE>
</UL><A NAME="77649"></A>
<UL><PRE>i++++; // error!
</PRE>
</UL><A NAME="77650"></A>
<P><A NAME="dingp48"></A>
The second reason is that double application of postfix increment almost never does what clients expect it to. As noted above, the second application of <CODE>operator++</CODE> in a double increment changes the value of the object returned from the first invocation, <I>not</I> the value of the original object. Hence, <NOBR>if<SCRIPT>create_link(48);</SCRIPT>
</NOBR></P>
<A NAME="77657"></A>
<UL><PRE>i++++;
</PRE>
</UL><A NAME="77658"></A><P><A NAME="dingp49"></A>
were legal, <CODE>i</CODE> would be incremented only once. This is counterintuitive and confusing (for both <CODE>int</CODE>s and <CODE>UPInt</CODE>s), so it's best <NOBR>prohibited.<SCRIPT>create_link(49);</SCRIPT>
</NOBR></p><A NAME="77659"></A>
<A NAME="p34"></A><P><A NAME="dingp50"></A>
C++ prohibits it for <CODE>int</CODE>s, but you must prohibit it yourself for classes you write. The easiest way to do this is to make the return type of postfix increment a <CODE>const</CODE> object. Then when compilers <NOBR>see<SCRIPT>create_link(50);</SCRIPT>
</NOBR></P>
<A NAME="77663"></A>
<UL><PRE>i++++; // same as i.operator++(0).operator++(0);
</PRE>
</UL><A NAME="77664"></A><P><A NAME="dingp51"></A>
they recognize that the <CODE>const</CODE> object returned from the first call to <CODE>operator++</CODE> is being used to call <CODE>operator++</CODE> again. <CODE>operator++</CODE>, however, is a non-<CODE>const</CODE> member function, so <CODE>const</CODE> objects — such as those returned from postfix <CODE>operator++</CODE> — can't call it.<A HREF="#8131" onMouseOver = "self.status = 'Link to Footnote 2'; return true" onMouseOut = "self.status = self.defaultStatus"><sup>2</sup></A> If you've ever wondered if it makes sense to have functions return <CODE>const</CODE> objects, now you know: sometimes it does, and postfix increment and decrement are examples. (For another example, turn to <A HREF="../EC/EC4_FR.HTM#6003" TARGET="_top" onMouseOver = "self.status = 'Link to Link to EC++ Item 21'; return true" onMouseOut = "self.status = self.defaultStatus">Item E21</A>.)<SCRIPT>create_link(51);</SCRIPT>
</p><A NAME="77673"></A>
<P><A NAME="dingp52"></A>
If you're the kind who worries about efficiency, you probably broke into a sweat when you first saw the postfix increment function. That function has to create a temporary object for its return value (see <A HREF="./MC4_FR.HTM#41177" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 19'; return true" onMouseOut = "self.status = self.defaultStatus">Item 19</A>), and the implementation above also creates an explicit temporary object (<CODE>oldValue</CODE>) that has to be constructed and destructed. The prefix increment function has no such temporaries. This leads to the possibly startling conclusion that, for efficiency reasons alone, clients of <CODE>UPInt</CODE> should prefer prefix increment to postfix increment unless they really need the behavior of postfix increment. Let us be explicit about this. When dealing with user-defined types, prefix increment should be used whenever possible, because it's inherently more <NOBR>efficient.<SCRIPT>create_link(52);</SCRIPT>
</NOBR></p><A NAME="7123"></A>
<P><A NAME="dingp53"></A>
Let us make one more observation about the prefix and postfix increment operators. Except for their return values, they do the same thing: they increment a value. That is, they're <I>supposed</I> to do the same thing. How can you be sure the behavior of postfix increment is consistent with that of prefix increment? What guarantee do you have that their implementations won't diverge over time, possibly as a result of different programmers maintaining and enhancing them? Unless you've followed the design principle embodied by the code above, you have no such guarantee. That principle is that postfix increment and decrement should be implemented <I>in terms of</I> their prefix counterparts. You then need only maintain the prefix versions, because the postfix versions will automatically behave in a consistent <NOBR>fashion.<SCRIPT>create_link(53);</SCRIPT>
</NOBR></p><A NAME="79205"></A>
<P><A NAME="dingp54"></A>
As you can see, mastering prefix and postfix increment and decrement is easy. Once you know their proper return types and that the postfix operators should be implemented in terms of the prefix operators, there's very little more to <NOBR>learn.<SCRIPT>create_link(54);</SCRIPT>
</NOBR></p>
<!-- SectionName="M7: Never overload &&,||, or ," -->
<A NAME="77702"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC2.HTM#5262">Item 6: Distinguish between prefix and postfix forms of increment and decrement operators</A> <BR> Continue to <A HREF="./MC2.HTM#33985">Item 8: Understand the different meanings of new and delete</A></FONT></DIV>
<A NAME="p35"></A><P><A NAME="dingp55"></A><font ID="mititle">Item 7: Never overload <CODE>&&</CODE>, <CODE>||</CODE>, or <CODE>,</CODE>.</font><SCRIPT>create_link(55);</SCRIPT>
</P>
<A NAME="6796"></A>
<A NAME="6798"></A>
<P><A NAME="dingp56"></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(56);</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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -