📄 index.html
字号:
<h2> <a name="Heading17"> Returning Objects by Value</a></h2>
<p>For the sake of efficiency, large objects are usually passed to -- or returned
from -- a function by reference or by their address. There are, however, a few
circumstances in which the best choice is still to return an object by value.
Operator <tt>+</tt> is an example of this situation. It has to return a result
object, but it cannot modify any of its operands. The seemingly natural choice
is to allocate the result object on the free store and return its address. Nevertheless,
this is not such a good idea. Dynamic memory allocation is significantly slower
than local storage. It also might fail and throw an exception, which then has
to be caught and handled. Even worse, this solution is error prone because it
is unclear who is responsible for deleting this object -- the creator or the
user?</p>
<p>Another solution is to use a static object and return it by reference. For
example</p>
<pre>
<tt>class Year</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> int year;</tt>
<tt>public:</tt>
<tt> Year(int y = 0) : year(y) {}</tt>
<tt> Year& operator + (const Year& other) const; //returns a reference to </tt>
<tt> //a local static Year</tt>
<tt> int getYear() const;</tt>
<tt> void setYear(int y);</tt>
<tt>};</tt>
<tt>Year& Year::operator + (const Year& other) const </tt>
<tt>{ </tt>
<tt> static Year result;</tt>
<tt> result = Year(this->getYear() + other.getYear() ); </tt>
<tt> return result;</tt>
<tt>}</tt>
</pre>
<p>Static objects solve the ownership problem, but they are still problematic:
On each invocation of the overloaded operator, the same instance of the static
object is being modified and returned to the caller. The same object can be
returned by reference to several more users, who do not know that they are holding
a shared instance that has just been modified behind their back.</p>
<p>Finally, the safest and most efficient solution is still to return the result
object by value:</p>
<pre>
<tt>class Year</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> int year;</tt>
<tt>public:</tt>
<tt> Year(int y = 0) : year(y) {}</tt>
<tt> Year operator + (const Year& other) const; //return Year object by value</tt>
<tt> int getYear() const;</tt>
<tt> void setYear(int y);</tt>
<tt>};</tt>
<tt>Year Year::operator + (const Year& other) const</tt>
<tt>{ </tt>
<tt> return Year(this->getYear() + other.getYear() ); </tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading18">Multiple Overloading</a></h2>
<p>Overloaded operators obey the rules of function overloading. Therefore, it
is possible to overload an operator more than once. When is it useful? Consider
the following <tt>Month</tt> class and its associated operator<tt> ==</tt>:</p>
<pre>
<tt>class Month</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> int m;</tt>
<tt>public:</tt>
<tt> Month(int m = 0); </tt>
<tt>};</tt>
<tt>bool operator == (const Month& m1, const Month &m2);</tt>
</pre>
<p>It is possible to use the overloaded operator <tt>==</tt> to compare a plain
<tt>int</tt> value and a <tt>Month</tt> object due to the implicit conversion
of <tt>int</tt> to <tt>Month</tt>. For example</p>
<pre>
<tt>void f()</tt>
<tt>{</tt>
<tt> int n = 7;</tt>
<tt> Month June(6);</tt>
<tt> bool same = </tt>
<tt> (June == n); //calls bool operator == (const Month& m1, const Month &m2);</tt>
<tt>}</tt>
</pre>
<p>This works fine, but it i's inefficient: The argument <tt>n</tt> is first converted
to a temporary <tt>Month</tt> object, which is then compared with the object
<tt>June</tt>. You can avoid the unnecessary construction of a temporary object
by defining additional overloaded versions of operator <tt>==</tt>:</p>
<pre>
<tt>bool operator == (int m, const Month& month);</tt>
<tt>bool operator == (const Month& month, int m);</tt>
</pre>
<p>Consequently, the expression <tt>June == n</tt> will now invoke the following
overloaded operator:</p>
<pre>
<tt>bool operator == (const Month& month, int m);</tt>
</pre>
<p>This overloaded version does not create a temporary object, so it's more efficient.
The same performance considerations led the C++ Standardization committee to
define three distinct versions of operator <tt>==</tt> for <tt>std::string</tt>
(see Chapter 10, ""STL and Generic Programming"") and other classes of the Standard
Library.' </p>
<h2> <a name="Heading19">Overloading Operators for Other User-Defined types</a></h2>
<p>You can overload an operator for <tt>enum</tt> types as well. For example,
it may be useful to overload operators such as ++ and <tt>--</tt> so that they
can iterate through the enumerator values of a given <tt>enum</tt> type. You
can do it like this:</p>
<pre>
<tt>#include <iostream></tt>
<tt>using namespace std;</tt>
<tt>enum Days </tt>
<tt>{</tt>
<tt> Monday, </tt>
<tt> Tuesday,</tt>
<tt> Wednesday, </tt>
<tt> Thursday, </tt>
<tt> Friday, </tt>
<tt> Saturday, </tt>
<tt> Sunday</tt>
<tt>};</tt>
<tt>Days& operator++(Days& d, int) // postfix ++</tt>
<tt>{</tt>
<tt> if (d == Sunday) </tt>
<tt> return d = Monday; //rollover</tt>
<tt> int temp = d; //convert to an int</tt>
<tt> return d = static_cast<Days> (++temp); </tt>
<tt>}</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> Days day = Monday;</tt>
<tt> for (;;) //display days as integers</tt>
<tt> {</tt>
<tt> cout<< day <<endl;</tt>
<tt> day++;</tt>
<tt> if (day == Sunday) </tt>
<tt> break;</tt>
<tt> }</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>If you prefer to view the enumerators in their symbolic representation rather
than as integers, you can overload the operator <tt><<</tt> as well:</p>
<pre>
<tt>ostream& operator<<(ostream& os, Days d) //display Days in symbolic form</tt>
<tt>{</tt>
<tt> switch</tt>
<tt> {</tt>
<tt> case Monday:</tt>
<tt> return os<<"Monday";</tt>
<tt> case Tuesday:</tt>
<tt> return os<<"Tuesday";</tt>
<tt> case Wednesday:</tt>
<tt> return os<<"Wednesday";</tt>
<tt> case Thursday:</tt>
<tt> return os<<"Thursady";</tt>
<tt> case Friday:</tt>
<tt> return os<<"Friday";</tt>
<tt> case Saturday:</tt>
<tt> return os<<"Satrurday";</tt>
<tt> case Sunday:</tt>
<tt> return os<<"Sunday";</tt>
<tt> default:</tt>
<tt> return os<<"Unknown";</tt>
<tt> } </tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading20">Overloading the Subscripts Operator</a></h2>
<p>For various classes that contain arrays of elements, it's handy to overload
the subscript operator to access a single element. Remember always to define
two versions of the subscript operator: a <tt>const</tt> version and a non-<tt>const</tt>
version. For example</p>
<pre>
<tt>class Ptr_collection</tt>
<tt>{</tt>
<tt>private :</tt>
<tt> void **ptr_array;</tt>
<tt> int elements;</tt>
<tt>public: </tt>
<tt> Ptr_collection() {}</tt>
<tt> //...</tt>
<tt> void * operator [] (int index) { return ptr_array[index];}</tt>
<tt> const void * operator [] (int index) const { return ptr_array[index];}</tt>
<tt>};</tt>
<tt>void f(const Ptr_collection & pointers)</tt>
<tt>{</tt>
<tt> const void *p = pointers[0]; //calls const version of operator [] </tt>
<tt> if ( p == 0) </tt>
<tt> return;</tt>
<tt> else</tt>
<tt> {</tt>
<tt> //...use p </tt>
<tt> }</tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading21">Function Objects</a></h2>
<p>A function object is implemented as a class that contains an overloaded version
of the function call operator. An instance of such a class can be used just
like a function. Ordinary functions can have any number of arguments; therefore,
operator <tt>()</tt> is exceptional among other operators because it can have
an arbitrary number of parameters. In addition, it can have default arguments.
In the following example, a function object implements a generic increment function:</p>
<pre>
<tt> #include <iostream></tt>
<tt>using namespace std;</tt>
<tt>class increment</tt>
<tt>{</tt>
<tt> //a generic increment function</tt>
<tt> public : template < class T > T operator() (T t) const { return ++t;}</tt>
<tt>};</tt>
<tt>void f(int n, const increment& incr)</tt>
<tt>{ </tt>
<tt> cout << incr(n); //output 1</tt>
<tt>}</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> int i = 0;</tt>
<tt> increment incr;</tt>
<tt> f(i, incr);</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<h2> <a name="Heading22">Conclusions</a></h2>
<p>The concept of operator overloading is neither new nor C++ exclusive. It is
one of the most fundamental facilities for implementing abstract data types
and compound classes. In many ways, overloaded operators in C++ behave like
ordinary member functions: They are inherited, they can be overloaded more than
once, and they can be declared as either nonstatic members or as nonmember functions.
However, several restrictions apply to overloaded operators. An overloaded operator
has a fixed number of parameters, and it cannot have default arguments. In addition,
the associativity and the precedence of an operator cannot be altered. Built-in
operators have an interface, consisting of the number of operands to which the
operator can be applied, whether the operator modifies any of its operands,
and result that is returned by the operator. When you are overloading an operator,
it is recommended that you conform to its built-in interface.</p>
<p>Conversion operators are a special type of overloaded operators. They differ
from ordinary overloaded operators in two respects: They do not have a return
value and they do not take any arguments. </p>
<p> </p>
<CENTER>
<P>
<HR>
<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> <BR>
<BR>
<BR>
<p></P>
<P>© <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. All
rights reserved.</p>
</CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -