Absolute C++ (4th Edition) part 34 pdf

10 277 0
Absolute C++ (4th Edition) part 34 pdf

Đang tải... (xem toàn văn)

Thông tin tài liệu

334 Operator Overloading, Friends, and References friend istream& operator >>(istream& inputStream, Percent& aPercent); friend ostream& operator <<(ostream& outputStream, const Percent& aPercent); //There would normally also be other members and friends. private: int value; }; O VERLOADING >> AND << The input and output operators >> and << can be overloaded just like any other operators. If you want the operators to behave as expected for cin, cout, and file I/O, then the value returned should be of type istream for input and ostream for output, and the value should be returned by reference. D ECLARATIONS class Class_Name { . . . public: . . . friend istream& operator >>(istream& Parameter_1 , Class_Name & Parameter_2 ); friend ostream& operator <<(ostream& Parameter_3 , const Class_Name & Parameter_4 ); . . . The operators do not need to be friends but cannot be members of the class being input or output. D EFINITIONS istream& operator >>(istream& Parameter_1 , Class_Name & Parameter_2) { . . . 08_CH08.fm Page 334 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 335 Tip W HAT M ODE OF R ETURNED V ALUE TO U SE A function can return a value of type T in four different ways: By plain old value, as in the function declaration T f( ); By constant value, as in the function declaration const T f( ); By reference, as in the function declaration T& f( ); By const reference, as in the function declaration const T& f( ); There is not unanimous agreement on which to use when. So, do not expect too much consistency in usage. Even when an author or programmer has a clear policy, they seldom manage to follow it without exception. Still, some points are clear. If you are returning a simple type, like int or char, there is no point in using a const when returning by value or by reference. So programmers typically do not use a const on the return type when it is a simple type. If you want the simple value returned to be allowed as an l-value, that is to be allowed on the left-hand side of an assignment statement, then return by reference; otherwise return the simple type by plain old value. Class types are not so simple. The rest of this discussion applies to returning an object of a class type. The decision on whether or not to return by reference has to do with whether or not you want to be able to use the returned object as an as an l-value. If you want to return something that can be used as an l-value, that is, that can be used on the left-hand side of an assignment operator, you must return by reference and so must use an ampere sign & on the returned type. Returning a local variable (or other short lived object) by reference, with or without a const, can produce problems and should be avoided. For class types, the two returned type specifications const T and const T& are very similar. They both mean that you cannot change the returned object by invoking some mutator function directly on the returned object, as in f( ).mutator( ); } ostream& operator <<(ostream& Parameter_3 , const Class_Name & Parameter_4 ) { . . . } If you have enough accessor and mutator functions, you can overload >> and << as nonfriend functions. However, it is natural and more efficient to define them as friends. 08_CH08.fm Page 335 Wednesday, August 13, 2003 1:02 PM 336 Operator Overloading, Friends, and References The returned value can still be copied to another variable with an assignment operator and that other variable can have the mutator function applied to it. If you cannot decide between the const T& and const T, use const T (without the ampersand). A const T& is perhaps a bit more efficient than a const T. 3 However, the difference is not typically that important and most pro- grammers use const T rather than const T& as a retuned type specification. As noted earlier, const T& can sometimes cause problems. The following summary may be of help. T is assumed to be a class type. Copy constructors are not covered until Chapter 10, but we include details about them here for reference value. If you have not yet read Chapter 10, simply ignore all references to copy constructors. If a public member function returns a private class member variable, it should always have a const on the returned type, as we explained in the pitfall 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 (One exception to this rule is that programmers normally always return a value of type string by ordinary value, not by const value. This is presumably because the type string is thought of as a simple type like int and char, even though string is a class type.) The following summary may be of help. T is assumed to be a class type. Simple retuning by value, as in the function declaration T f( ); Cannot be used as an l-value, and the returned value can be changed directly as in f( ).mutator( ). Calls the copy constructor. Returning by constant value, as in const T f( ); This case is the same as the previous case, but the returned value cannot be changed directly as in f( ).mutator( ). Returning by reference as in T& f( ); Can be used as an l-value, and the returned value can be changed directly as in f( ).muta- tor( ) . Does not call the copy constructor. Returning by constant reference, as in const T& f( ); Cannot be used as an l-value, and the returned value cannot be changed directly as in f( ).mutator( ). Does not call the copy constructor. ■ THE ASSIGNMENT OPERATOR If you overload the assignment operator =, you must overload it as a member operator. If you do not overload the assignment operator =, then you automatically get an assign- ment operator for your class. This default assignment operator copies the values of member variables from one object of the class to the corresponding member variables of another object of the class. For simple classes, that is usually what you want. When we discuss pointers, this default assignment operator will not be what we want, and we will discuss overloading the assignment operator at that point. 3 This is because const T& does not call the copy constructor while const T does call the copy constructor. Copy constructors are discussed in Chapter 10. 08_CH08.fm Page 336 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 337 Self-Test Exercises ■ OVERLOADING THE INCREMENT AND DECREMENT OPERATORS The increment and decrement operators ++ and each have two versions. They can do different things depending on whether they are used in prefix notation, ++x, or post- fix (suffix) notation, x++. Thus, when overloading these operators, you need to some- how distinguish between 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 (and maybe even on second reading) seems a bit contrived. If you overload the ++ operator in the regular way (as a nonmem- ber operator with one parameter or as a member operator with no parameters), then you have overloaded the prefix form. To obtain the postfix version, x++ or x , you add a second parameter of type int. This is just a marker for the compiler; you do not give a second int argument when you invoke x++ or x For example, Display 8.6 contains the definition of a class whose data is pairs of integers. The increment operator ++ is defined so it works in both prefix and postfix notation. We have defined ++ so that it has the intuitive spirit of ++ on int variables. This is the best way to define ++, but you are free to define it to return any kind of type and perform any kind of action. The definition of the postfix version ignores that int parameter, as shown in Display 8.6. When the compiler sees a++, it treats it as an invocation of IntPair::opera- tor++(int) , with a as the calling object. The increment and decrement operator on simple types, such as int and char, return by reference in the prefix form and by value in the postfix form. If you want to emulate what happens with simple types when you overload these operators for your class types, then you would return by reference for the prefix form and by value for the postfix form. However, we find it opens the door to too many problems to return by reference with increment or decrement operators, and so we always simply return by value for all versions of the increment and decrement operators. 12. Is the following legal? Explain your answer. (The definition of IntPair is given in Display 8.6.) IntPair a(1,2); (a++)++; ■ OVERLOADING THE ARRAY OPERATOR [ ] You can overload the square brackets, [], for a class so that they can be used with objects of the class. If you want to use [] in an expression on the left-hand side of an assignment operator, then the operator must be defined to return a reference. When overloading [], the operator [] must be a member function. pre fi x an d postfix return b y reference 08_CH08.fm Page 337 Wednesday, August 13, 2003 1:02 PM 338 Operator Overloading, Friends, and References Display 8.6 Overloading ++ (part 1 of 2) 1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 class IntPair 5 { 6 public: 7 IntPair(int firstValue, int secondValue); 8 IntPair operator++( ); //Prefix version 9 IntPair operator++(int); //Postfix version 10 void setFirst(int newValue); 11 void setSecond(int newValue); 12 int getFirst( ) const; 13 int getSecond( ) const; 14 private: 15 int first; 16 int second; 17 }; 18 int main( ) 19 { 20 IntPair a(1,2); 21 cout << "Postfix a++: Start value of object a: "; 22 cout << a.getFirst( ) << " " << a.getSecond( ) << endl; 23 IntPair b = a++; 24 cout << "Value returned: "; 25 cout << b.getFirst( ) << " " << b.getSecond( ) << endl; 26 cout << "Changed object: "; 27 cout << a.getFirst( ) << " " << a.getSecond( ) << endl; 28 a = IntPair(1, 2); 29 cout << "Prefix ++a: Start value of object a: "; 30 cout << a.getFirst( ) << " " << a.getSecond( ) << endl; 31 IntPair c = ++a; 32 cout << "Value returned: "; 33 cout << c.getFirst( ) << " " << c.getSecond( ) << endl; 34 cout << "Changed object: "; 35 cout << a.getFirst( ) << " " << a.getSecond( ) << endl; 36 return 0; 37 } 38 39 IntPair::IntPair(int firstValue, int secondValue) 40 : first(firstValue), second(secondValue) 41 {/*Body intentionally empty*/} You need not give a parameter name in a function or operator declaration. For ++ it makes sense to give no parameter since the parameter is not used. 08_CH08.fm Page 338 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 339 Display 8.6 Overloading ++ (part 2 of 2) 42 IntPair IntPair::operator++(int ignoreMe) //Postfix version 43 { 44 int temp1 = first; 45 int temp2 = second; 46 first++; 47 second++; 48 return IntPair(temp1, temp2); 49 } 50 IntPair IntPair::operator++( ) //Prefix version 51 { 52 first++; 53 second++; 54 return IntPair(first, second); 55 } 56 void IntPair::setFirst(int newValue) 57 { 58 first = newValue; 59 } 60 void IntPair::setSecond(int newValue) 61 { 62 second = newValue; 63 } 64 int IntPair::getFirst( ) const 65 { 66 return first; 67 } 68 int IntPair::getSecond( ) const 69 { 70 return second; 71 } S AMPLE D IALOGUE Postfix a++: Start value of object a: 1 2 Value returned: 1 2 Changed object: 2 3 Prefix ++a: Start value of object a: 1 2 Value returned: 2 3 Changed object: 2 3 08_CH08.fm Page 339 Wednesday, August 13, 2003 1:02 PM 340 Operator Overloading, Friends, and References It may help to review the syntax for the operator [], since it is different from any other operator we have seen. Remember that [] is overloaded as a member operator; therefore one thing in an expression using [] must be the calling object. In the expres- sion a[2], a is the calling object and 2 is the argument to the member operator []. When overloading [], this “index” parameter can be any type. For example, in Display 8.7 we define a class called Pair whose objects behave like arrays of characters with the two indexes 1 and 2 (not 0 and 1). Note that the expres- sions a[1] and a[2] behave just like array indexed variables. If you look at the defini- tion of the overloaded operator [], you will see that a reference is returned and that it is a reference to a member variable, not to the entire Pair object. This is because the member variable is analogous to an indexed variable of an array. When you change a[1] (in the sample code in Display 8.7), you want that to be a change to the member variable first. Note that this gives access to the private member variables to any pro- gram, for example, via a[1] and a[2] in the sample main function in Display 8.7. Although first and second are private members, the code is legal because it does not reference first and second by name but indirectly using the names a[1] and a[2]. Display 8.7 Overloading [] (part 1 of 2) 1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 class CharPair 5 { 6 public: 7 CharPair( ){/*Body intentionally empty*/} 8 CharPair(char firstValue, char secondValue) 9 : first(firstValue), second(secondValue) 10 {/*Body intentionally empty*/} 11 12 char& operator[](int index); 13 private: 14 char first; 15 char second; 16 }; 17 int main( ) 18 { 19 CharPair a; 20 a[1] = ’A’; 21 a[2] = ’B’; 08_CH08.fm Page 340 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 341 ■ OVERLOADING BASED ON L-VALUE VERSUS R-VALUE Although we will not be doing it in this book, you can overload a function name (or operator) so that it behaves differently when used as an l-value and when it is used as an r-value. (Recall that an l-value means it can be used on the left-hand side of an assign- ment statement.) . For example, if you want a function f to behave differently depend- ing on whether it is used as an l-value or an r-value, you can do so as follows: Display 8.7 Overloading [] (part 2 of 2) 22 cout << "a[1] and a[2] are:\n"; 23 cout << a[1] << a[2] << endl; 24 cout << "Enter two letters (no spaces):\n"; 25 cin >> a[1] >> a[2]; 26 cout << "You entered:\n"; 27 cout << a[1] << a[2] << endl; 28 return 0; 29 } 30 31 //Uses iostream and cstdlib: 32 char& CharPair::operator[](int index) 33 { 34 if (index == 1) 35 return first; 36 else if (index == 2) 37 return second; 38 else 39 { 40 cout << "Illegal index value.\n"; 41 exit(1); 42 } 43 } S AMPLE D IALOGUE a[1] and a[2] are: AB Enter two letters (no spaces): CD You entered: CD Note that you return the member variable, not the entire Pair object, because the member variable is analogous to an indexed variable of an array. 08_CH08.fm Page 341 Wednesday, August 13, 2003 1:02 PM 342 Operator Overloading, Friends, and References class SomeClass { public: int& f( ); // will be used in any l-value invocation const int& f( ) const; // used in any r-value invocation }; The two parameter lists need not be empty, but they should be the same (or else you just get simple overloading). Be sure to notice that the second declarations of f has two occurrences of const. You must include both occurrences of const. The ampersand signs & are of course also required. ■ Operators, such as + and ==, can be overloaded so that they can be used with objects of a class type that you define. ■ An operator is just a function that uses a different syntax for invocations. ■ A friend function of a class is an ordinary function except that it has access to the private members of the class, just like member functions do. ■ When an operator is overloaded as a member of a class, the first operand is the call- ing object. ■ If your classes each have a full set of accessor functions, then the only reason to make a function a friend is to make the definition of the friend function simpler and more efficient, but that is often reason enough. ■ A reference is a way of naming a variable. It is essentially an alias for the variable. ■ When overloading the >> or << operators, the type returned should be a stream type and should be a reference, which is indicated by appending an & to the name of the returned type. ANSWERS TO SELF-TEST EXERCISES 1. The difference between a (binary) operator (such as +, *, or /) and a function involves the syn- tax of how they are called. In a function call, the arguments are given in parentheses after the function name. With an operator the arguments are given before and after the operator. Also, you must use the reserved word operator in the operator declaration and in the definition of an overloaded operator. 2. Add the following declaration and function definition: bool operator <(const Money& amount1, const Money& amount2); bool operator <(const Money& amount1, const Money& amount2) { Chapter Summary 08_CH08.fm Page 342 Wednesday, August 13, 2003 1:02 PM Answers to Self-Test Exercises 343 int dollars1 = amount1.getDollars( ); int dollars2 = amount2.getDollars( ); int cents1 = amount1.getCents( ); int cents2 = amount2.getCents( ); return ((dollars1 < dollars2) || ((dollars1 == dollars2) && (cents1 < cents2))); } 3. When overloading an operator, at least one of the arguments to the operator must be of a class type. This prevents changing the behavior of + for integers. 4. If you omit the const at the beginning of the declaration and definition of the overloaded plus operator for the class Money, then the following is legal: (m1 + m2) = m3; If the definition of the class Money is as shown in Display 8.1, so that the plus operator returns by const value, then it is not legal. 5. const Money Money::operator -(const Money& secondOperand) const { int allCents1 = cents + dollars*100; int allCents2 = secondOperand.cents + secondOperand.dollars*100; int diffAllCents = allCents1 - allCents2; int absAllCents = abs(diffAllCents); int finalDollars = absAllCents/100; int finalCents = absAllCents%100; if (diffAllCents < 0) { finalDollars = -finalDollars; finalCents = -finalCents; } return Money(finalDollars, finalCents); } 6. A friend function and a member function are alike in that they both can use any member of the class (either public or private) in their function definition. However, a friend function is defined and used just like an ordinary function; the dot operator is not used when you call a friend function and no type qualifier is used when you define a friend function. A mem- ber function, on the other hand, is called using an object name and the dot operator. Also, a member function definition includes a type qualifier consisting of the class name and the scope resolution operator, ::. 08_CH08.fm Page 343 Wednesday, August 13, 2003 1:02 PM . CharPair a; 20 a[1] = ’A’; 21 a[2] = ’B’; 08_CH08.fm Page 340 Wednesday, August 13, 2003 1:02 PM References and More Overloaded Operators 341 ■ OVERLOADING BASED ON L-VALUE VERSUS R-VALUE Although. variable is analogous to an indexed variable of an array. 08_CH08.fm Page 341 Wednesday, August 13, 2003 1:02 PM 342 Operator Overloading, Friends, and References class SomeClass { public:. const Money& amount2) { Chapter Summary 08_CH08.fm Page 342 Wednesday, August 13, 2003 1:02 PM Answers to Self-Test Exercises 343 int dollars1 = amount1.getDollars( ); int dollars2 = amount2.getDollars(

Ngày đăng: 04/07/2014, 05:21

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan