Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 40 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
40
Dung lượng
500,93 KB
Nội dung
320 Operator Overloading, Friends, and References ■ FRIEND FUNCTIONS If your class has a full set of accessor and mutator functions, you can use the accessor and mutator functions to define nonmember overloaded operators (as in Display 8.1 as opposed to Display 8.2). However, although this may give you access to the private member variables, it may not give you efficient access to them. Look again at the defi- nition of the overloaded addition operator + given in Display 8.1. Rather than just reading four member variables, it must incur the overhead of two invocations of get- Cents and two invocations of getDollars. This adds a bit of inefficiency and also can make the code harder to understand. The alternative of overloading + as a member gets around this problem at the price of losing automatic type conversion of the first oper- and. Overloading the + operator as a friend will allow us to both directly access mem- ber variables and have automatic type conversion for all operands. A friend function of a class is not a member function of the class, but it has access to the private members of that class (to both private member variables and private member functions) just as a member function does. To make a function a friend func- tion, you must name it as a friend in the class definition. For example, in Display 8.3 we have rewritten the definition of the class Money yet another time. This time we have overloaded the operators as friends. You make an operator or function a friend of a class by listing the operator or function declaration in the definition of the class and placing the keyword friend in front of the operator or function declaration. A friend operator or friend function has its declaration listed in the class definition, just as you would list the declaration of a member function, except that you precede the declaration by the keyword friend. However, a friend is not a member function; rather, it really is an ordinary function with extraordinary access to the data members of the class. The friend is defined exactly like the ordinary function it is. In particular, the oper- ator definitions shown in Display 8.3 do not include the qualifier Money:: in the func- tion heading. Also, you do not use the keyword friend in the function definition (only in the function declaration). The friend operators in Display 8.3 are invoked just like the nonfriend, nonmember operators in Display 8.1, and they have automatic type con- version of all arguments just like the nonfriend, nonmember operators in Display 8.1. The most common kinds of friend functions are overloaded operators. However, any kind of function can be made a friend function. A function (or overloaded operator) can be a friend of more than one class. To make it a friend of multiple classes, just give the declaration of the friend function in each class for which you want it to be a friend. Many experts consider friend functions (and operators) to be in some sense not “pure.” They feel that in the true spirit of object-oriented programming all operators and functions should be member functions. On the other hand, overloading operators as friends provides the pragmatic advantage of automatic type conversion in all argu- ments, and since the operator declaration is inside the class definitions, it provides at least a bit more encapsulation than nonmember, nonfriend operators. We have shown you three ways to overload operators: as nonmember nonfriends, as members, and as friends. You can decide for yourself which technique you prefer. friend function 08_CH08.fm Page 320 Wednesday, August 13, 2003 1:02 PM Friend Functions and Automatic Type Conversion 321 Display 8.3 Overloading Operators as Friends (part 1 of 2) 1 #include <iostream> 2 #include <cstdlib> 3 #include <cmath> 4 using namespace std; 5 //Class for amounts of money in U.S. currency 6 class Money 7 { 8 public: 9 Money( ); 10 Money(double amount); 11 Money(int dollars, int cents); 12 Money(int dollars); 13 double getAmount( ) const; 14 int getDollars( ) const; 15 int getCents( ) const; 16 void input( ); //Reads the dollar sign as well as the amount number. 17 void output( ) const; 18 friend const Money operator +(const Money& amount1, const Money& amount2); 19 friend const Money operator -(const Money& amount1, const Money& amount2); 20 friend bool operator ==(const Money& amount1, const Money& amount2); 21 friend const Money operator -(const Money& amount); 22 private: 23 int dollars; //A negative amount is represented as negative dollars and 24 int cents; //negative cents. Negative $4.50 is represented as -4 and -50. 25 int dollarsPart(double amount) const; 26 int centsPart(double amount) const; 27 int round(double number) const; 28 }; 29 int main( ) 30 { 31 < If the main function is the same as in Display 8.1, then the screen dialogue will be the same as shown in Display 8.1. > 32 } 33 34 const Money operator +(const Money& amount1, const Money& amount2) 35 { 36 int allCents1 = amount1.cents + amount1.dollars*100; 37 int allCents2 = amount2.cents + amount2.dollars*100; 38 int sumAllCents = allCents1 + allCents2; 39 int absAllCents = abs(sumAllCents); //Money can be negative. 40 int finalDollars = absAllCents/100; 08_CH08.fm Page 321 Wednesday, August 13, 2003 1:02 PM 322 Operator Overloading, Friends, and References Display 8.3 Overloading Operators as Friends (part 2 of 2) 41 int finalCents = absAllCents%100; 42 if (sumAllCents < 0) 43 { 44 finalDollars = -finalDollars; 45 finalCents = -finalCents; 46 } 47 return Money(finalDollars, finalCents); 48 } 49 const Money operator -(const Money& amount1, const Money& amount2) 50 < The complete definition is Self-Test Exercise 7. > 51 bool operator ==(const Money& amount1, const Money& amount2) 52 { 53 return ((amount1.dollars == amount2.dollars) 54 && (amount1.cents == amount2.cents)); 55 } 56 const Money operator -(const Money& amount) 57 { 58 return Money(-amount.dollars, -amount.cents); 59 } < Definitions of all other member functions are the same as in Display 8.1. > 60 Note that friends have direct access to member variables. F RIEND F UNCTIONS A friend function of a class is an ordinary function except that it has access to the private members of objects of that class. To make a function a friend of a class, you must list the function declaration for the friend function in the class definition. The function declaration is preceded by the keyword friend. The function declaration may be placed in either the private section or the public section, but it will be a public function in either case, so it is clearer to list it in the public section. S YNTAX OF A C LASS D EFINITION WITH F RIEND F UNCTIONS class Class_Name { public: friend Declaration_for_Friend_Function_1 friend Declaration_for_Friend_Function_2 . . . You need not list the friend functions first. You can intermix the order of these declarations. 08_CH08.fm Page 322 Wednesday, August 13, 2003 1:02 PM Friend Functions and Automatic Type Conversion 323 Pitfall C OMPILERS WITHOUT F RIENDS On some C++ compilers friend functions simply do not work as they are supposed to work. Worst of all, they may work sometimes and not work at other times. On these compilers friend functions do not always have access to private members of the class as they are supposed to. Presumably, this will be fixed in later releases of these compilers. In the meantime, you will have to work around this problem. If you have one of these compilers for which friend functions do not work, you must either use accessor functions to define nonmember functions and overloaded operators or you must overload operators as members. ■ FRIEND CLASSES A class can be a friend of another class in the same way that a function can be a friend of a class. If the class F is a friend of the class C, then every member function of the class F is a friend of the class C. To make one class a friend of another, you must declare the friend class as a friend within the other class. Member_Function_Declarations private: Private_Member_Declarations }; E XAMPLE class FuelTank { public: friend void fillLowest(FuelTank& t1, FuelTank& t2); //Fills the tank with the lowest fuel level, or t1 if a tie. FuelTank(double theCapacity, double theLevel); FuelTank( ); void input( ); void output( ) const; private: double capacity;//in liters double level; }; A friend function is not a member function. A friend function is defined and called the same way as an ordinary function. You do not use the dot operator in a call to a friend function, and you do not use a type qualifier in the definition of a friend function. friend class 08_CH08.fm Page 323 Wednesday, August 13, 2003 1:02 PM 324 Operator Overloading, Friends, and References Self-Test Exercises When one class is a friend of another class, it is typical for the classes to reference each other in their class definitions. This requires that you include a forward declara- tion to the class defined second, as illustrated in the outline that follows this paragraph. Note that the forward declaration is just the heading of the class definition followed by a semicolon. If you want the class F to be a friend of the class C, the general outline of how you set things up is as follows: class F; //forward declaration class C { public: friend class F; }; class F { Complete examples using friend classes are given in Chapter 17. We will not be using friend classes until then. 6. What is the difference between a friend function for a class and a member function for the class? 7. Complete the definition of the friend subtraction operator - in Display 8.3. 8. Suppose you wish to overload the operator < so that it applies to the type Money defined in Display 8.3. What do you need to add to the definition of Money given in Display 8.3? References and More Overloaded Operators Do not mistake the pointing finger for the moon. Zen Saying This section covers some specialized, but important, overloading topics, including overloading the assignment operator and the <<, >>, [], ++, and operators. Because forward declaration 8.3 08_CH08.fm Page 324 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 325 you need to understand returning a reference to correctly overload some of these oper- ators, we also discuss this topic. ■ REFERENCES A reference is the name of a storage location. 2 You can have a standalone reference, as in the following: int robert; int& bob = robert; This makes bob a reference to the storage location for the variable robert, which makes bob an alias for the variable robert. Any change made to bob will also be made to rob- ert . Stated this way, it sounds like a standalone reference is just a way to make your code confusing and get you in trouble. In most instances, a standalone reference is just R ULES ON O VERLOADING O PERATORS ■ When overloading an operator, at least one parameter (one operand) of the resulting overloaded operator must be of a class type. ■ Most operators can be overloaded as a member of the class, a friend of the class, or a nonmember, nonfriend. ■ The following operators can only be overloaded as (nonstatic) members of the class: =, [], ->, and ( ). ■ You cannot create a new operator. All you can do is overload existing operators such as +, -, *, /, %, and so forth. ■ You cannot change the number of arguments that an operator takes. For exam- ple, you cannot change % from a binary to a unary operator when you overload %; you cannot change ++ from a unary to a binary operator when you overload it. ■ You cannot change the precedence of an operator. An overloaded operator has the same precedence as the ordinary version of the operator. For example, x*y + z always means (x*y) + z, even if x, y, and z are objects and the operators + and * have been overloaded for the appropriate classes. ■ The following operators cannot be overloaded: the dot operator (.), the scope resolution operator ( ::), sizeof, ?:, and the operator .*, which is not discussed in this book. ■ An overloaded operator cannot have default arguments. 2 If you know about pointers, you will notice that a reference sounds like a pointer. A reference is essentially, but not exactly, a constant pointer. There are differences between pointers and ref- erences, and they are not completely interchangeable. reference 08_CH08.fm Page 325 Wednesday, August 13, 2003 1:02 PM 326 Operator Overloading, Friends, and References trouble, although there a few cases where it can be useful. We will not discuss standal- one references any more, nor will we use them. As you may suspect, references are used to implement the call-by-reference parame- ter mechanism. So, the concept is not completely new to this chapter, although the phrase a reference is new. We are interested in references here because returning a reference will allow you to overload certain operators in a more natural way. Returning a reference can be viewed as something like returning a variable or, more precisely, an alias to a variable. The syn- tactic details are simple. You add an & to the return type. For example: double& sampleFunction(double& variable); Since a type like double& is a different type from double, you must use the & in both the function declaration and the function definition. The return expression must be something with a reference, such as a variable of the appropriate type. It cannot be an expression, such as X + 5. Although many compilers will let you do it (with unfortu- nate results), you also should not return a local variable because you would be generat- ing an alias to a variable and immediately destroying the variable. A trivial example of the function definition is double& sampleFunction(double& variable) { return variable; } Of course, this is a pretty useless, even troublesome, function, but it illustrates the con- cept. For example, the following code will output 99 and then 42: double m = 99; cout << sampleFunction(m) << endl; sampleFunction(m) = 42; cout << m << endl; We will only be returning a reference when defining certain kinds of overloaded operators. L-V ALUES AND R-V ALUES The term l-value is used for something that can appear on the left-hand side of an assignment operator. The term r-value is used for something that can appear on the right-hand side of an assignment operator. If you want the object returned by a function to be an l-value, it must be returned by reference. 08_CH08.fm Page 326 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 327 Pitfall R ETURNING A R EFERENCE TO C ERTAIN M EMBER V ARIABLES When a member function returns a member variable and that member variable is of some class type, then it should normally not be returned by reference. For example, consider class A { public: const SomeClass getMember( ) { return member; } private: SomeClass member; }; where SomeClass is, of course, some class type. The function getMember should not return a reference, but should instead return by const value as we have done in the example. The problem with returning a reference to a class type member variable is the same as what we described for returning the member variable by non-const value in the programming tip section of this chapter entitled RR RR ee ee tt tt uu uu rr rr nn nn ii ii nn nn gg gg MM MM ee ee mm mm bb bb ee ee rr rr VV VV aa aa rr rr ii ii aa aa bb bb ll ll ee ee ss ss oo oo ff ff aa aa CC CC ll ll aa aa ss ss ss ss TT TT yy yy pp pp ee ee When returning a member variable which is itself of some class type it should normally be returned by const value. (Every such rule has rare exceptions.) ■ OVERLOADING >> AND << The operators >> and << can be overloaded so that you can use them to input and out- put objects of the classes you define. The details are not that different from what we have already seen for other operators, but there are some new subtleties. The insertion operator << that we used with cout is a binary operator very much like + or For example, consider the following: cout << "Hello out there.\n"; The operator is <<, the first operand is the predefined object cout (from the library iostream), and the second operand is the string value "Hello out there.\n". The pre- defined object cout is of type ostream, and so when you overload <<, the parameter that receives cout will be of type ostream. You can change either of the two operands to <<. When we cover file I/O in Chapter 12 you will see how to create an object of type ostream that sends output to a file. (These file I/O objects as well as the objects cin and cout are called streams, which is why the library name is ostream.) The overloading that we create, with cout in mind, will turn out to also work for file output without any changes in the definition of the overloaded <<. In our previous definitions of the class Money (Display 8.1 through Display 8.3) we used the member function output to output values of type Money. This is adequate, but << is an operator stream overloading 08_CH08.fm Page 327 Wednesday, August 13, 2003 1:02 PM 328 Operator Overloading, Friends, and References it would be nicer if we could simply use the insertion operator << to output values of type Money, as in the following: Money amount(100); cout << "I have " << amount << " in my purse.\n"; instead of having to use the member function output as shown below: Money amount(100); cout << "I have "; amount.output( ); cout << " in my purse.\n"; One problem in overloading the operator << is deciding what value, if any, should be returned when << is used in an expression like the following: cout << amount The two operands in the above expression are cout and amount, and evaluating the expression should cause the value of amount to be written to the screen. But if << is an operator like + or -, then the above expression should also return some value. After all, expressions with other operands, such as n1 + n2, return values. But what does cout << amount return? To obtain the answer to that question, we need to look at a more com- plicated expression involving <<. Let’s consider the following expression, which involves evaluating a chain of expres- sions using <<: cout << "I have " << amount << " in my purse.\n"; If you think of the operator << as being analogous to other operators, such as +, then the above should be (and in fact is) equivalent to the following: ( (cout << "I have ") << amount ) << " in my purse.\n"; What value should << return to make sense of the above expression? The first thing evaluated is the subexpression: (cout << "I have ") If things are to work out, then the above subexpression had better return cout so that the computation can continue as follows: ( cout << amount ) << " in my purse.\n"; And if things are to continue to work out, (cout << amount) had better also return cout so that the computation can continue as follows: cout << " in my purse.\n"; chains of << 08_CH08.fm Page 328 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 329 This is illustrated in Display 8.4. The operator << should return its first argument, which is of type ostream (the type of cout). Thus, the declaration for the overloaded operator << (to use with the class Money) should be as follows: class Money { public: . . . friend ostream& operator <<(ostream& outs, const Money& amount); << returns a stream Display 8.4 << as an Operator cout << "I have " << amount << " in my purse.\n"; means the same as ((cout << "I have ") << amount) << " in my purse.\n"; and is evaluated as follows: First evaluate (cout << "I have "), which returns cout: ((cout << "I have ") << amount) << " in my purse.\n"; (cout << amount) << " in my purse.\n"; Then evaluate (cout << amount), which returns cout: (cout << amount) << " in my purse.\n"; cout << " in my purse.\n"; Then evaluate cout << " in my purse.\n", which returns cout: cout << " in my purse.\n"; cout; The string "I have" is output. The string "in my purse.\n" is output. The value of amount is output. Since there are no more << operators, the process ends. 08_CH08.fm Page 329 Wednesday, August 13, 2003 1:02 PM [...]... representing strings, which C++ inherited from the C programming language These sorts of strings are called C-strings Although C-strings are an older way of representing strings, it is difficult to do any sort of string processing in C++ without at least passing contact with C-strings For example, quoted strings, such as "Hello", are implemented as C-strings in C++ The ANSI/ISO C++ standard includes a more... (1668) This section describes one way to represent strings of characters, which C++ inherited from the C language Section 9.3 describes a string class that is a more modern way to represent strings Although the string type described here may be a bit “old fashioned,” it is still widely used and is an integral part of the C++ language s C-STRING VALUES AND C-STRING VARIABLES One way to represent a string... quotient of two integers For example, 1/2, 3/4, 64/2, and so forth are all rational numbers (By 1/2 and so on we mean the everyday fraction, not the integer division this expression would produce in a C++ program.) Represent rational numbers as two values of type int, one for the numerator and one for the denominator Call the class Rational Include a constructor with two arguments that can be used to... cents Negative $4.50 is represented as -4 and -50 int dollarsPart(double amount) const; int centsPart(double amount) const; int round(double number) const; }; int main( ) { Money yourAmount, myAmount(10, 9); cout > yourAmount; cout . PM Friend Functions and Automatic Type Conversion 323 Pitfall C OMPILERS WITHOUT F RIENDS On some C++ compilers friend functions simply do not work as they are supposed to work. Worst of all, they. const; 27 int round(double number) const; 28 }; 29 int main( ) 30 { 31 Money yourAmount, myAmount(10, 9); 32 cout << "Enter an amount of money: "; 33 cin >> yourAmount; 34 cout. the prefix and postfix versions so that you can have two versions of the overloaded operator. In C++ this distinction between prefix and postfix versions is handled in a way that at first reading