📄 mc2.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML>
<HEAD>
<TITLE>More Effective C++ | Chapter 2: Operators</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>
var dingbase = "MC2_DIR.HTM";
var dingtext = "MEC++ Operators, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="MEC++ Chapter Intro: Operators" -->
<A NAME="65246"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC1_FR.HTM#5218" TARGET="_top" onMouseOver = "self.status = 'Back to MEC++ Item 4'; return true" onMouseOut = "self.status = self.defaultStatus">Item 4: Avoid gratuitous default constructors</A> <BR> Continue to <A HREF="./MC2.HTM#5970" onMouseOver = "self.status = 'Continue to Item 5: Be wary of user-defined conversion functions'; return true" onMouseOut = "self.status = self.defaultStatus">Item 5: Be wary of user-defined conversion functions</A></FONT></DIV>
<A NAME="5701"></A><A NAME="p24"></A><P><A NAME="dingp1"></A><font ID="mgtitle">Operators</font><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="65247"></A>
<P><A NAME="dingp2"></A>
Overloadable operators — you gotta love 'em! They allow you to give your types the same syntax as C++'s built-in types, yet they let you put a measure of power into the functions <I><I>behind</I></I> the operators that's unheard of for the built-ins. Of course, the fact that you can make symbols like "<CODE>+</CODE>" and "<CODE>==</CODE>" do anything you want also means you can use overloaded operators to produce programs best described as impenetrable. Adept C++ programmers know how to harness the power of operator overloading without descending into the <NOBR>incomprehensible.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P><A NAME="65290"> </A>
<P><A NAME="dingp3"></A>
Regrettably, it is easy to make the descent. Single-argument constructors and implicit type conversion operators are particularly troublesome, because they can be invoked without there being any source code showing the calls. This can lead to program behavior that is difficult to understand. A different problem arises when you overload operators like <CODE>&&</CODE> and <CODE>||</CODE>, because the shift from built-in operator to user-defined function yields a subtle change in semantics that's easy to overlook. Finally, many operators are related to one another in standard ways, but the ability to overload operators makes it possible to violate the accepted <NOBR>relationships.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P><A NAME="11143"> </A>
<P><A NAME="dingp4"></A>
In the items that follow, I focus on explaining when and how overloaded operators are called, how they behave, how they should relate to one another, and how you can seize control of these aspects of overloaded operators. With the information in this chapter under your belt, you'll be overloading (or <I><I>not</I></I> overloading) operators like a <NOBR>pro.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></p>
<!-- SectionName="M5: Be wary of user-defined conversion functions" -->
<A NAME="5970"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC2.HTM#5701">Operators</A> <BR> Continue to <A HREF="./MC2.HTM#5262">Item 6: Distinguish between prefix and postfix forms of increment and decrement operators</A></FONT></DIV>
<P><A NAME="dingp5"></A><font ID="mititle">Item 5: Be wary of user-defined conversion functions.</font><SCRIPT>create_link(5);</SCRIPT>
</P>
<A NAME="72112"></A>
<A NAME="34647"></A>
<P><A NAME="dingp6"></A>
C++ allows compilers to perform implicit conversions between types. In honor of its C heritage, for example, the language allows silent conversions from <CODE>char</CODE> to <CODE>int</CODE> and from <CODE>short</CODE> to <CODE>double</CODE>. This is why you can pass a <CODE>short</CODE> to a function that expects a <CODE>double</CODE> and still have the call succeed. The more frightening conversions in C — those that may lose <A NAME="p25"></A>information — are also present in C++, including conversion of <CODE>int</CODE> to <CODE>short</CODE> and <CODE>double</CODE> to (of all things) <CODE>char</CODE>.<SCRIPT>create_link(6);</SCRIPT>
</P> <A NAME="6331"></A>
<P><A NAME="dingp7"></A>
You can't do anything about such conversions, because they're hard-coded into the language. When you add your own types, however, you have more control, because you can choose whether to provide the functions compilers are allowed to use for implicit type <NOBR>conversions.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P> <A NAME="6333"></A>
<P><A NAME="dingp8"></A>
Two kinds of functions allow compilers to perform such conversions: <I>single-argument constructors</I> and <I>implicit type conversion operators</I>. A single-argument constructor is a constructor that may be called with only one argument. Such a constructor may declare a single parameter or it may declare multiple parameters, with each parameter after the first having a default value. Here are two <NOBR>examples:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="34650"></A>
<UL><PRE>class Name { // for names of things
public:
Name(const string& s); // converts string to
// Name
...
<A NAME="74625"></A>
};
<A NAME="34652"></A>
class Rational { // for rational numbers
public:
Rational(int numerator = 0, // converts int to
int denominator = 1); // Rational
...
<A NAME="74626"></A>
};</PRE>
</UL>
<A NAME="34655"></A>
<P><A NAME="dingp9"></A>
An implicit type conversion operator is simply a member function with a strange-looking name: the word <CODE>operator</CODE> followed by a type specification. You aren't allowed to specify a type for the function's return value, because the type of the return value is basically just the name of the function. For example, to allow <CODE>Rational</CODE> objects to be implicitly converted to <CODE>double</CODE>s (which might be useful for mixed-mode arithmetic involving <CODE>Rational</CODE> objects), you might define class <CODE>Rational</CODE> like <NOBR>this:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="34656"></A>
<UL><PRE>class Rational {
public:
...
operator double() const; // converts Rational to
}; // double</PRE>
</UL>
<A NAME="34657"></A>
<P><A NAME="dingp10"></A>
This function would be automatically invoked in contexts like <NOBR>this:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></p><A NAME="34658"></A>
<UL><PRE>Rational r(1, 2); // r has the value 1/2
<A NAME="34659"></A>
double d = 0.5 * r; // converts r to a double,
// then does multiplication</PRE>
</UL>
<A NAME="34662"></A>
<A NAME="p26"></A><P><A NAME="dingp11"></A>
Perhaps all this is review. That's fine, because what I really want to explain is why you usually don't want to provide type conversion functions of <I>any</I> <NOBR>ilk.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P> <A NAME="34663"></A>
<P><A NAME="dingp12"></A>
The fundamental problem is that such functions often end up being called when you neither want nor expect them to be. The result can be incorrect and unintuitive program behavior that is maddeningly difficult to <NOBR>diagnose.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P> <A NAME="34664"></A>
<P><A NAME="dingp13"></A>
Let us deal first with implicit type conversion operators, as they are the easiest case to handle. Suppose you have a class for rational numbers similar to the one above, and you'd like to print <CODE>Rational</CODE> objects as if they were a built-in type. That is, you'd like to be able to do <NOBR>this:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="34665"></A>
<UL><PRE>Rational r(1, 2);
<A NAME="34666"></A>
cout << r; // should print "1/2"</PRE>
</UL>
<A NAME="34667"></A>
<P><A NAME="dingp14"></A>
Further suppose you forgot to write an <CODE>operator<<</CODE> for <CODE>Rational</CODE> objects. You would probably expect that the attempt to print <CODE>r</CODE> would fail, because there is no appropriate <CODE>operator<<</CODE> to call. You would be mistaken. Your compilers, faced with a call to a function called <CODE>operator<<</CODE> that takes a <CODE>Rational</CODE>, would find that no such function existed, but they would then try to find an acceptable sequence of implicit type conversions they could apply to make the call succeed. The rules defining which sequences of conversions are acceptable are complicated, but in this case your compilers would discover they could make the call succeed by implicitly converting <CODE>r</CODE> to a <CODE>double</CODE> by calling <CODE>Rational</CODE>::<CODE>operator</CODE> <CODE>double</CODE>. The result of the code above would be to print <CODE>r</CODE> as a floating point number, not as a rational number. This is hardly a disaster, but it demonstrates the disadvantage of implicit type conversion operators: their presence can lead to the <I>wrong function</I> being called (i.e., one other than the one <NOBR>intended).<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P> <A NAME="34670"></A>
<P><A NAME="dingp15"></A>
The solution is to replace the operators with equivalent functions that don't have the syntactically magic names. For example, to allow conversion of a <CODE>Rational</CODE> object to a <CODE>double</CODE>, replace <CODE>operator</CODE> <CODE>double</CODE> with a function called something like <CODE>asDouble</CODE>:<SCRIPT>create_link(15);</SCRIPT>
</P>
<A NAME="34671"></A>
<UL><PRE>class Rational {
public:
...
double asDouble() const; // converts Rational
}; // to double</PRE>
</UL><A NAME="34672"></A>
<A NAME="dingp16"></A><P><A NAME="dingp16"></A>Such a member function must be called <NOBR>explicitly:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="34673"></A>
<UL><PRE>Rational r(1, 2);
<A NAME="34674"></A>
cout << r; // error! No operator<<
// for Rationals
<A NAME="34675"></A><A NAME="p27"></A>
cout << r.asDouble(); // fine, prints r as a
// double</PRE>
</UL>
<A NAME="34676"></A>
<P><A NAME="dingp17"></A>
In most cases, the inconvenience of having to call conversion functions explicitly is more than compensated for by the fact that unintended functions can no longer be silently invoked. In general, the more experience C++ programmers have, the more likely they are to eschew type conversion operators. The members of <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver="self.status='ISO/ANSI Standardization Committee Home Page'; return true" onMouseOut="self.status=self.defaultStatus" target="_top">the</NOBR> committee</A> working on the standard C++ library (see <A HREF="../EC/EC7_FR.HTM#8392" TARGET="_top" onMouseOver = "self.status = 'Link to Link to EC++ Item 49'; return true" onMouseOut = "self.status = self.defaultStatus">Item E49</A> and <A HREF="./MC6_FR.HTM#5473" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 35'; return true" onMouseOut = "self.status = self.defaultStatus">Item 35</A>), for example, are among the most experienced in the business, and perhaps that's why the <CODE>string</CODE> type they added to the library contains no implicit conversion from a <CODE>string</CODE> object to a C-style <CODE>char*</CODE>. Instead, there's an explicit member function, <CODE>c_str</CODE>, that performs that conversion. Coincidence? I think <NOBR>not.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P> <A NAME="65154"></A>
<P><A NAME="dingp18"></A>
Implicit conversions via single-argument constructors are more difficult to eliminate. Furthermore, the problems these functions cause are in many cases worse than those arising from implicit type conversion <NOBR>operators.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P> <A NAME="65199"></A>
<P><A NAME="dingp19"></A>
As an example, consider a class template for array objects. These arrays allow clients to specify upper and lower index <NOBR>bounds:<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="65108"></A>
<UL><PRE>template<class T>
class Array {
public:
Array(int lowBound, int highBound);
Array(int size);
<A NAME="65109"></A>
T& operator[](int index);
<A NAME="65110"></A>
...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -