📄 ch03.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD> <META NAME="Author" Content="Steph Mineart"> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <TITLE>ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading </TITLE> <link rel="stylesheet" TYPE="text/css" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>ANSI/ISO C++ Professional Programmer's Handbook</H1></CENTER><CENTER> <P><A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <HR></CENTER><H1 align="center"> 3</H1><h1 align="center"> Operator Overloading</h1><address>by Danny Kalev</address><ul> <li><a href="#Heading1">Introduction</a> <li><a href="#Heading2">Operator Overloading Rules of Thumb</a> <ul> <li><a href="#Heading3">Members and Nonmembers</a> <li><a href="#Heading4">Operator's Interface</a> <li><a href="#Heading5"> Operator Associativity</a> </ul> <li><a href="#Heading6">Restrictions on Operator Overloading</a> <ul> <li><a href="#Heading7">Operators Can Only Be Overloaded for User-Defined Types </a> <li><a href="#Heading8">Invention of New Operators Is Not Allowed</a> <li><a href="#Heading9"> Precedence and Argument Number</a> <li><a href="#Heading10">Default Parameters</a> <li><a href="#Heading11">Operators That Cannot Be Overloaded</a> </ul> <li><a href="#Heading12">Conversion Operators</a> <ul> <li><a href="#Heading13">Standard Versus User-Defined Conversions</a> </ul> <li><a href="#Heading14">Postfix and Prefix Operators</a> <li><a href="#Heading15">Using Function Call Syntax</a> <li><a href="#Heading16">Consistent Operator Overloading</a> <li><a href="#Heading17"> 'Returning Objects by Value</a> <li><a href="#Heading18">Multiple Overloading</a> <li><a href="#Heading19">Overloading Operators for Other User-Defined types</a> <li><a href="#Heading20">Overloading the Subscripts Operator</a> <li><a href="#Heading21">Function Objects</a> <li><a href="#Heading22">Conclusions</a> </ul><hr size=4><h2> <a name="Heading1">Introduction</a></h2><p>A built-in operator can be extended to support user-defined types as well. Such an extension <i>overloads</i> the predefined meaning of an operator rather than overrides it. Although ordinary functions can offer the same functionality, operator overloading provides a uniform notational convention that is clearer than the ordinary function call syntax. For example</p><pre><tt>Monday < Tuesday; //overloaded < </tt><tt>Greater_than(Monday, Tuesday); </tt></pre><p>The history of operator overloading can be traced back to the early days of Fortran. Fortran, the first high-level programming language, presented the concept of operator overloading in a way that was revolutionary back in the mid-1950s. For the first time, built-in operators such as <tt>+</tt> or <tt>-</tt> could be applied to various data types: integers, real and complex. Until then, assembly languages -- which didn't even support operator notation -- had been the only choice for programmers. Fortran's operator overloading was limited to a fixed set of built-in data types; they could not be extended by the programmer. Object-based programming languages offered user-defined overloaded operators. In such languages, it is possible to associate a set of operators with a user-defined type. Object-oriented languages usually incorporate operator overloading as well.</p><p>The capability to redefine the meaning of a built-in operator in C++ was a source of criticism. People -- mostly C programmers making the migration to C++ -- felt that overloading an operator was as dangerous as enabling the programmer to add, remove, or change keywords of the language. Still, notwithstanding the potential Tower of Babel that might arise as a result, operator overloading is one of the most fundamental features of C++ and is mandatory for generic programming (generic programming is discussed in Chapter 10, "STL and Generic Programming."). Today, even languages that tried to make do without operator overloading are in the process of adding this feature.</p><p>This chapter explores the benefits as well as the potential problems of operator overloading. It also discusses the few restrictions that apply to operator overloading. Finally, it presents conversion operators, which are a special form of overloaded operators.</p><p>An overloaded operator is essentially a function whose name is an operator preceded by the keyword <tt>operator</tt>. For example</p><pre><tt>class Book</tt><tt>{</tt><tt>private:</tt><tt> long ISBN;</tt><tt>public:</tt><tt>//...</tt><tt> long get_ISBN() const { return ISBN;}</tt><tt>};</tt><tt>bool operator < (const Book& b1, const Book& b2) // overload operator <</tt><tt>{</tt><tt> return b1.get_ISBN() < b2.get_ISBN();</tt><tt>} </tt></pre><h2> <a name="Heading2">Operator Overloading Rules of Thumb</a></h2><p>C++ enforces few restrictions on operator overloading. For instance, it does not prohibit a programmer from overloading the operator <tt>++</tt> in order to perform a decrement operation on its object (to the dismay of its users, who would instead expect operator <tt>++</tt> to perform an increment operation). Such misuses and puns can lead to a cryptic coding style that is almost unintelligible. Often, the source code that contains the definition of an overloaded operator is not accessible to its users; therefore, overloading an operator in an unexpected, nonintuitive manner is not recommended.</p><p>The other extreme, avoiding operator overloading altogether, is not a practical choice either because it means giving up an important tool for data abstraction and generic programming. When you are overloading an operator to support a user-defined type, therefore, it is recommended that you adhere to the basic semantics of the corresponding built-in operator. In other words, an overloaded operator has the same side effects on its operands and manifests the same interface as does the corresponding built-in operator.</p><h3> <a name="Heading3">Members and Nonmembers</a></h3><p>Most of the overloaded operators can be declared either as nonstatic class members or as nonmember functions. In the following example, the operator <tt>==</tt> is overloaded as a nonstatic class member:</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> int day;</tt><tt> int month;</tt><tt> int year;</tt><tt>public:</tt><tt> bool operator == (const Date & d ); // 1: member function</tt><tt>};</tt></pre><p>Alternatively, it can be declared as a <tt>friend</tt> function (the criteria for choosing between a member and a friend will be discussed later in this chapter):</p><pre><tt>bool operator ==( const Date & d1, const Date& d2); // 2: nonmember function</tt><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> int day;</tt><tt> int month; </tt><tt> int year;</tt><tt>public:</tt><tt> friend bool operator ==( const Date & d1, const Date& d2);</tt><tt>};</tt></pre><p>Nonetheless, the operators <tt>[]</tt>, <tt>()</tt>, <tt>=</tt>, and <tt>-></tt> can only be declared as nonstatic member functions; this ensures that their first operand is an lvalue.</p><h3> <a name="Heading4">Operator's Interface</a></h3><p>When you overload an operator, adhere to the interface of its built-in counterpart. The interface of an operator consists of the number of operands to which it applies, whether any of these operands can be altered by the operator, and the result that is returned by the operator. For example, consider operator <tt>==</tt>. Its built-in version can be applied to a wide variety of fundamental types, including <tt>int</tt>, <tt>bool</tt>, <tt>float</tt>, and <tt>char</tt>, and to pointers. The underlying computation process that is required for testing whether the operands of operator <tt>==</tt> are equal is an implementation-detail. However, it can be generalized that the built-in <tt>==</tt> operator tests its left and right operands for equality and returns a <tt>bool</tt> value as its result. It is important to note also that operator <tt>==</tt> does not modify any of its operands; in addition, the order of the operands is immaterial in the case of operator <tt>==</tt>. An overloaded operator <tt>==</tt> should conform to this behavior, too.</p><h3> <a name="Heading5"> Operator Associativity</a></h3><p>Operator <tt>==</tt> is binary and symmetrical. An overloaded version of <tt>==</tt> conforms to these qualities. It takes two operands, which are of the same type. Indeed, one can use operator <tt>==</tt> to test the equality of two operands of distinct fundamental types, for example <tt>char</tt> and <tt>int</tt>. However, C++ automatically applies <i>integral promotion</i> to the operands in this case; as a result, the seemingly distinct types are promoted to a single common type before they are compared.<i> </i>The symmetrical quality implies that an overloaded operator <tt>==</tt> is to be defined as a <tt>friend</tt> function rather than a member function. So that you can see why, here's a comparison of two different versions of the same overloaded operator <tt>==</tt>:</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> int day;</tt><tt> int month; </tt><tt> int year;</tt><tt>public:</tt><tt> Date();</tt><tt> bool operator == (const Date & d) const; // 1 asymmetrical</tt><tt> friend bool operator ==(const Date& d1, const Date& d2); //2 symmetrical</tt><tt>};</tt><tt>bool operator ==(const Date& d1, const Date& d2);</tt></pre><p>The overloaded operator <tt>==</tt> that is declared as a member function in <tt>(1)</tt> is inconsistent with the built-in operator <tt>==</tt> because it takes two arguments of different types. The compiler transforms the member operator <tt>==</tt> into the following:</p><pre><tt>bool Date::operator == (const Date *const, const Date&) const; </tt></pre><p>The first argument is a <tt>const this</tt> pointer that points to a <tt>const</tt> object (remember that <tt>this</tt> is always a <tt>const</tt> pointer; it points to a <tt>const</tt> object when the member function is also <tt>const)</tt>. The second argument is a reference to <tt>const Date</tt>. Clearly, these are two distinct types for which no standard conversion exists. On the other hand, the <tt>friend</tt> version takes two arguments of the same type. There are practical implications to favoring the <tt>friend</tt> version over the member function. STL algorithms rely on a symmetrical version of the overloaded operator <tt>==</tt>. For example, containers that store objects that do not have symmetrical operator <tt>==</tt> cannot be sorted.</p><p>Another example, built-in operator <tt>+=</tt>, which also takes two operands, modifies its left operand but leaves the right operand unchanged. The interface of an overloaded <tt>+=</tt> needs to reflect the fact that it modifies its object but not its right operand. This is reflected by the declaration of the function parameter as <tt>const</tt>, whereas the function itself is a non-<tt>const</tt> member function. For example</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt> int day;</tt><tt> int month; </tt><tt> int year;</tt><tt>public:</tt><tt> Date();</tt><tt> //built-in += changes its left operand but not its right one</tt><tt> //the same behavior is maintained here</tt><tt> Date & operator += (const Date & d);</tt><tt>};</tt></pre><p>To conclude, it can be said that every overloaded operator must implement an interface that is similar to the one that is manifested by the built-in operator. The implementer is free to define the underlying operation and hide its details -- as long as it conforms to the interface.</p><h2> <a name="Heading6">Restrictions on Operator Overloading</a></h2><p>As was previously noted, an overloaded operator is a function that is declared with the <tt>operator</tt> keyword, immediately followed by an <i>operator id.</i> An operator id can be one of the following:</p><pre><tt>new delete new[] delete[]</tt><tt>+ - * / % ^ & | ~</tt><tt>! = < > += -= *= /= %=</tt><tt>^= &= |= << >> >>= <<= == !=</tt><tt><= >= && || ++ -- , ->* -></tt><tt>() []</tt></pre><p>In addition, the following operators can be overloaded both in their unary and binary forms:</p><pre><tt>+ - * &</tt></pre><p>Overloaded operators are inherited in the same manner as other base class functions.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -