1. Trang chủ
  2. » Công Nghệ Thông Tin

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

10 284 0
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 172,53 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Tip W HAT M ODE OF R ETURNED V ALUE TO U SEA 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

Trang 1

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) {

.

Trang 2

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

Trang 3

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 Reeeettttuu R u urrrrnn n n iiiin n ng g g M M Meeeem m mb b beeeerrrr V V Vaaa arrrriiiiaaa ab blllleeeessss ooo b offff aaa a CCC Cllllaaa assssssss TTTTyyy yp p peeee (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.

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 assignassign-ment 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

3This 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

Trang 4

Self-Test Exercises

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 (sufpost-fix) 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 memnonmem-ber 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++)++;

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

prefix and postfix

return by reference

Trang 5

Display 8.6 Overloading ++ (part 1 of 2)

1 #include <iostream>

2 #include <cstdlib>

3 using namespace std;

4 class IntPair

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;

18 int main( )

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;

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.

Trang 6

Display 8.6 Overloading ++ (part 2 of 2)

42 IntPair IntPair:: operator ++( int ignoreMe) //Postfix version

44 int temp1 = first;

45 int temp2 = second;

46 first++;

47 second++;

48 return IntPair(temp1, temp2);

50 IntPair IntPair:: operator ++( ) //Prefix version

52 first++;

53 second++;

54 return IntPair(first, second);

56 void IntPair::setFirst( int newValue)

58 first = newValue;

60 void IntPair::setSecond( int newValue)

62 second = newValue;

64 int IntPair::getFirst( ) const

66 return first;

68 int IntPair::getSecond( ) const

70 return second;

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

Trang 7

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

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;

17 int main( )

19 CharPair a;

20 a[1] = ’A’;

21 a[2] = ’B’;

Trang 8

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 stateassign-ment.) 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;

30

31 //Uses iostream and cstdlib:

32 char & CharPair:: operator []( int index)

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 }

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

Trang 9

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

Trang 10

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, ::

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

TỪ KHÓA LIÊN QUAN