Chapter 8 Operator Overloaing, friends and References

advertisement
Chapter 8
Operator Overloading, Friends and References
0. Introduction
Here many of the tools necessary for defining abstract data types are presented. The
notions of operator overloading, functions that are friends of classes, classes that are
friends of classes and automatic type conversion are all important to the construction of
Abstract Data types.
1. Outline of topics in the chapter
8.1 Basic Operator Overloading
Overloading Basics
Returning by const Value
Overloading Unary Operators
Overloading as Member Functions
Overloading Function Application ()
8.2 Friend Functions and Automatic Type Conversion
Constructors for Automatic Type Conversion
Friend Functions
Friend Classes
8.3 References and More Overloaded Operators
References
Overloading >> and <<
The Assignment Operator
Overloading the increment and decrement Operators
Overloading the Array Operator []
Overloading based on l-value versus r-value
2. General remarks on the chapter
8.1 Basic Operator Overloading
Overloading Basics
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 2
These are a few remarks on ideas that I had trouble getting straight my first time through
C++ and operator overloading, so I’m collecting them together here.
Recall that few operations (+, /, -, ==, <, etc.) apply to class objects directly.
We do have assignment, =, that results in member wise copy. This means the right hand
object’s members are each assigned to the corresponding left-hand object’s members. We
have seen that frequently this should be called member-UN-wise copy, since under
circumstances frequently encountered, this results in a disaster.
C++ provides operator overloading to extend the ability to program closer to the
problem domain. If you can add objects in the problem domain, you can code your class
so that you can add objects in the solution domain that represent the problem objects.
To prevent mischief, C++ requires that at least one argument of an overloaded operator
be a class object. Operators are overloaded using an “operator function” having its name
made up of the keyword operator followed by the operator symbol to be overloaded.
The operator function must be a member of a class (in which case the calling object is an
argument for the operator) or the operator function must have a class object as at least
one of its arguments.
Most operators can be overloaded, only a few cannot. Specifically, the operators ., .*,
?:, and :: may not be overloaded. There are a few operators that must be overloaded as
non-static member functions, namely =, ->, (), [].
When an operator is overloaded, only operators that exist can be overloaded: No new
tokens can be formed. For example, you cannot create an exponentiation operator ** as in
Fortran. Only the behavior of operators can be changed by overloading. The arity of an
operator cannot be changed. Arity (the number of arguments an operator takes, that is,
whether the operator is binary or unary) and the precedence of an operator cannot be
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 3
changed by overloading. For example, the “not” operator, operator!, is a unary a
prefix operator and cannot be made into a binary operator by overloading.
Let us revisit the class Money. The text implements the overloading of operator==
as an ordinary function,
bool operator==(const Money& amt1, const Money& amt2);
The text points out that this requires access functions, but it turns out that access
functions provide at best inefficient access, if not insufficient access. The operator
function needs friend access. (See Section 8.2 of the text, and below in this document.)
While you can call the operator== function directly, the intention of operator
overloading is that you behave as if the == operator is part of the language, defined for
Money objects. When the compiler sees
leftHandMoneyObject == RightHandMoneyObject
the expression is translated into a call to the operator function:
operator==(leftHandMoneyObject, RightHandMoneyObject)
The bool return value is inserted in the place for the expression just as it would be for
any primitive expression such as x == y.
Overloading as Member Functions
When a binary operator is overloaded as a member function, the operator’s left argument
is always the calling object. The operator function must have one argument in this case.
Having the operator function as member is convenient for access to class internals, but it
prevents a measure of flexibility in symmetric treatment of overloading.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 4
When a binary operator is overloaded as a stand-alone function, the operator function
must have two arguments. The left-hand operand of the expression is the first argument
for the operator function and the right-hand operand of the expression is the second
argument for the operator function.
Overloading Unary Operators
When a unary operator is overloaded as a member function, the only operand is the
calling object. This is true regardless of whether the operator is prefix or postfix. The
compiler will take care of that detail. (But see the discussion of overloading the postfix
++ operator, later.)
When a unary operator is overloaded as a stand-alone function, the argument of the
operator is the argument for the operator function. For example,
class A; //A is defined somewhere else
A a;
!a . . . //compiler translates this into operator!(a)
//for a stand-alone overloaded operator
Overloading Function Application ()
Overloading this operator implements function call syntax for class objects. It can have
default arguments. If we overload using
class T
{
public:
. . .
void operator()(T t);
. . .
};
Where T1 and T2 are classes, then the function calls syntax,
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 5
T v, w;
u(v);
is translated into a function call
u.operator()(v);
Given a call in a setting where there are several overloaded operator functions that may
match, the “best match” rules for function overload resolution are used to find the
operator function to be used.
8.2 Friend Functions and Automatic Type Conversion
A friend function has access to all members of a class, regardless of access control
(public, protected or private). A constructor with one argument provides automatic type
conversion.
Constructors for Automatic Type Conversion
If you use a different type than the expected type in a function call, the compiler will look
for a conversion from the type provided to the expected type. Constructors having one
argument are candidates for this conversion. This conversion is applied to arguments for
overloaded operator functions as well as to other functions.
Friend Functions
As we noted in the introduction, a friend has full access to every member of the class.
The status of friend is granted by the class by declaring the function within the class
with the keyword friend in front of the declaration. A friend of a class is not a
member. The access keywords public, private or protected have no effect on
friend a declaration, so where in the class you place the declaration is immaterial.
When should an operation be a friend function and when is making the function a
member the better choice for an operation? Actually, the question should be what is the
minimum access that still does the job. Some operations must be members, things like
constructors, destructors, and certain overloaded operators. Typically, there is a choice
for most of the remainder of the functions we need to write. The idea is to make the set of
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 6
functions with access to the class as small as possible. Experts advise not using friends
except to avoid global data, global (non-member functions) or public data members.
The next section gives a setting where there is good reason for using friend functions.
Member versus friend overloading
There is a compelling argument in favor of overloading operators using friends rather
than overloading as members. If both operands are function arguments, then C++ will do
automatic type conversion of either argument to the class type from simple types such as
int (provided you have supplied an appropriate constructor). This allows you to use 2
as a Rational (see Programming problem 2) or an amount of money in an expression such
as
object + 2
or
2 + object
which are converted to
operator+( object, 2 );
or
operator+( 2, object );
where the non class type arguments will be converted to an object using the constructor
having an int argument. However, if operator+ is a member, then the first argument
must always be an object,
as in:
object + 2
and expressions such as
2 + object
are always illegal. This is the reason for presenting the friend rather than member
operator overloading first.
Friend Classes
Sometimes, there is a need for all functions of one class to be friends of another class.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 7
Declaring class F to be a friend of a class C makes each member functions of
be F a friend of class C.
There is often a choice between declaring a class into a friend of another class and
making the class a member of the second class. Clearly, classes should be made friends
only when the two classes represent closely related entities in the problem being solved.
8.3 References and More Overloaded Operators
References
A reference is another name, that is, an alias, for an object. A reference is very nearly an
automatically dereferenced, constant pointer, but pointers and references are not
interchangeable.
The main use for references is passing parameters to functions, function return values,
and for overloaded operators. If T is a type, the expression T& x; defines x to be a
reference to a type T object, and requires initialization. We do not care for use of standalone references, nevertheless, we write the following as an illustration.
int i;
int& intRef = i;
Any use of intRef as an l-value or as an r-value will have the same effect as using the
int variable i. Since use of these standalone references mostly provides opportunity
for error, we weight in against this use.
When we use a reference variable as a parameter, the function call mechanism initializes
the reference to refer to the caller’s argument (which must be an l-value.)
Overloading >> and <<
If we are the class author, we can put a member overloading
operator <<(ourclass object)
in our class definition. The istream and ostream classes are not ours to modify.
It would not be desirable for us to do so, even if we could, since every one else will want
her (or his) class to have i/o in the iostream library.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 8
The answer is to write a stand-alone operator function that overloads our inserter or
extractor, where the iostream is the first argument, and our class object is the
second argument. This provides us with what we need:
in_stream >> our_class_object;
and
out_stream << our_class_object;
When we overload operator << for output, the return value is a reference to the
output stream so that we can have chain of output statements. The text illustrates this
quite clearly on pages 328 and following. I will not elaborate further.
Similarly, when we overload operator >> for input, the return value is a reference to
the input stream so that we can have chain of input statements.
I used a void return type for an input overloading of operator >>, then attempted to
write a chain of input statements. The error messages were not particularly enlightening.
Borland gives: Not an allowed type in function main()
cin >> x >> y; // Not an allowed type in function main()
Overloaded friend operator functions interact badly using VC++6.0 prior to installing
Patch Level 5. There is no good work around except not to use friends with this compiler.
I strongly encourage that the patch file from Microsoft be downloaded and installed.
Operator Overloading using void return type
While the text always overloads the << and >> operators to return a stream reference,
some instructors prefer to arrange an operator overloading to output to a stream from a
class to return a void than to return a stream reference. While this provides somewhat
simpler code in the implementation, the result violates the philosophy of the language
design. It is the intent of the language design that an operator overloaded for a class
should behave as it does for predefined types.
Whether the operator function returns a stream reference or a void is a matter of
whether we want to have the following style of i/o available to us:
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 9
our_class x, y, z;
in_stream >> x >> y >> z;
If we are content to write instead,
in_stream >> x;
in_stream >> y;
in_stream >> z;
The difference in coding amounts to replacing the return type with void, and removing
the return statement from the function. The sole advantage is not having to return the
stream.
// returns istream&:
istream& operator >> (istream& ins, Money& amount)
{
//all the necessary code to fetch the amount
//from the istream and do some format checking
//are in Display 8.8 in the text.
return ins;
}
void operator >> (istream& ins, Money& amount)
{
//all the necessary code to fetch the amount
//from the istream and do some format checking
//are in Display 8.8 in the text.
//no return statement...
}
Similar arrangements work for overloading operator <<.
The Assignment Operator
If no operator= is provided, the compiler will generate an operator= that copies
members. If you have only primitive values for class variables, this is what you want.
Otherwise, the programmer had to write an overloaded opreator=. The assignment
operator, operator=, must be overloaded as a non-static class member. The text waits
until pointers are discussed before treating overloading the assignment operator.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 10
Overloading the increment and decrement Operators
If ++ or -- is overloaded as a unary operator, the operator becomes a prefix operator. To
get a postfix operator overloading, you have to treat the operator overloading as if it were
a binary operator with an int second argument. For example,
// operator overloaded as a member
class A
{
public:
// other members
A operator++(int);
// . . .
};
A A::operator++(int);
// operator overloaded as a friend.
class A
{
public:
// other members
friend A operator++(A&, int);
//. . .
};
A operator++(A& a, int i)
{
//code to carry out the increment
}
When the postfix-overloaded operator is used, it is illegal to put an int value following
the postfix ++ or -- operator. If you can call the operator function directly, you must
put an int argument in for the int parameter.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 11
Overloading the Array Operator []
The index operator, [], must be overloaded as a non-static member function of the class.
When overloading the index operator, the text says that the parameter must be an integer
type. This is a simplification. In fact, any type can be used. This enables associative
arrays (that the text does not treat).
Overloading based on l-value versus r-value
The technique for distinguishing between use of a returned value as an l-value from use
its use as an r-value was sought for some time during the development of C++. There are
times when it is necessary to make this distinction. For real examples, see the
implementation of the indexing operators in the Standard Template Library.
3. Solutions to, and remarks on, selected Programming Projects
1. Modify class Money from Display 8.5
Modify the definition of class Money from Display 8.5, page 331, by
a) adding overloaded operators <, <=, > and >= for Money objects (Hint: Self-Test
exercise 8)
b) adding a member function with this declaration (to be placed in the class definition and
implemented)
const Money percent(int percentFigure) const;
//Returns percentFigure percent of Money.
//example: If percentFigure is 10, a Money object is returned that
//represents 0.1 of the amount of money of the calling object
//If purse is a Money object representing $100.20, purse.percent(10)
//is a Money object representing $10.02.
Modify the definition of class Money from Display 8.5, page 331, by
a) adding overloaded operators <, <=, > and >=
for Money objects
(Hint: Self-Test exercise 8)
b) adding a member function with this declaration (to be placed in the
class definition and implemented)
const Money percent(int percentFigure) const;
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 12
//Returns percentFigure percent of Money.
//example: If percentFigure is 10, a Money object is returned that
//represents 0.1 of the amount of money of the calling object
//If purse is a Money object representing $100.20, purse.percent(10)
//is a Money object representing $10.02.
*/
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
//Class for amounts of money in U.S. currency.
class Money
{
public:
Money( );
Money(double amount);
Money(int theDollars, int theCents);
Money(int theDollars);
double getAmount( ) const;
int getDollars( ) const;
int getCents( ) const;
//Programming Problem 1 a)
friend bool operator<(const Money& amt1, const Money& amt2);
friend bool operator<=(const Money& amt1, const Money& amt2);
friend bool operator>(const Money& amt1, const Money& amt2);
friend bool operator>=(const Money& amt1, const Money& amt2);
// Part 1 b)
const Money percent(int percentFigure) const;
//Returns percentFigure percent of Money.
//Example: If percentFigure is 10, a Money object is returned that
//represents 0.1 of the amount of money of the calling object
//If purse is a Money object representing $100.20, purse.percent(10)
//is a Money object representing $10.02.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 13
friend const Money operator +(const Money& amount1, const Money&
amount2);
friend const Money operator -(const Money& amount1, const Money&
amount2);
friend bool operator ==(const Money& amount1, const Money& amount2);
friend const Money operator -(const Money& amount);
friend ostream& operator <<(ostream& outputStream, const Money&
amount);
friend istream& operator >>(istream& inputStream, Money& amount);
private:
int dollars; //negative amounts are represented as negative dollars
int cents;
//and negative cents. Negative $4.50 is represented as -
4
//(dollars) and -50 (cents)
int dollarsPart(double amount) const;
int centsPart(double amount) const;
int round(double number) const;
};
int main( )
{
Money yourAmount, myAmount(10, 9);
cout << "Enter an amount of money: ";
cin >> yourAmount;
int percentAmount;
cout << "Enter a percent you wish to take of your amount \n";
cin >> percentAmount;
cout << "Your amount is " << yourAmount << endl;
cout << "My amount is " << myAmount << endl;
cout << percentAmount << "% of your amount is "
<< yourAmount.percent(percentAmount)
<< endl;
cout << "15% of my amount is " << myAmount.percent(15)
<< endl << endl;
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 14
if (yourAmount == myAmount)
cout << "We have the same amounts.\n";
else
cout << "One of us is richer.\n";
Money ourAmount = yourAmount + myAmount;
cout << yourAmount << " + " << myAmount
<< " equals " << ourAmount << endl;
Money diffAmount = yourAmount - myAmount;
cout << yourAmount << " - " << myAmount
<< " equals " << diffAmount << endl;
cout << "Your amount is " << yourAmount << endl;
cout << "My amount is " << myAmount << endl;
if(yourAmount >= myAmount)
cout << "Your amount is greater than or equal to my Amount\n";
else
cout << "You have less money than I do\n";
if(ourAmount > diffAmount)
cout << "The sum of our Amounts is greater than the difference.\n";
else
cout << "Something is terribly wrong.\n";
if(yourAmount <= myAmount)
cout << "Your amount is less than or equal to my Amount\n";
else
cout << "Your amount is greater than my Amount\n";
if(ourAmount < diffAmount)
cout << "The sum of our Amounts is less than the difference..\n";
else
cout << "The sum of our Amounts is greater than the difference.\n";
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 15
return 0;
}
//Implementations for Programming Problem 1 a)
bool operator<(const Money& amt1, const Money& amt2)
{
return (amt1.dollars < amt2.dollars) ||
((amt1.dollars == amt2.dollars) &&
(amt1.cents < amt2.cents));
}
//This is < (above logic) OR == (logic from operator ==, below)
bool operator<=(const Money& amt1, const Money& amt2)
{
return ((amt1.dollars < amt2.dollars) ||
((amt1.dollars == amt2.dollars) &&
(amt1.cents < amt2.cents)))
||
((amt1.dollars == amt2.dollars)
&& (amt1.cents == amt2.cents));
}
bool operator>(const Money& amt1, const Money& amt2)
{
return (amt1.dollars > amt2.dollars) ||
((amt1.dollars == amt2.dollars) &&
(amt1.cents > amt2.cents));
}
bool operator>=(const Money& amt1, const Money& amt2)
{
return ((amt1.dollars > amt2.dollars) ||
((amt1.dollars == amt2.dollars) &&
(amt1.cents > amt2.cents)))
||
((amt1.dollars == amt2.dollars)
&& (amt1.cents == amt2.cents));
}
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 16
// Implemenation of Part 1 b)
const Money Money::percent(int pcnt) const
{
int d = dollars * pcnt / 100;
int c = dollars * pcnt % 100 + cents * pcnt / 100;
return Money(d, c);
}
//Text's implementation from Display 8.5
const Money operator +(const Money& amount1, const Money& amount2)
{
int allCents1 = amount1.cents + amount1.dollars*100;
int allCents2 = amount2.cents + amount2.dollars*100;
int sumAllCents = allCents1 + allCents2;
int absAllCents = abs(sumAllCents); //Money can be negative.
int finalDollars = absAllCents/100;
int finalCents = absAllCents%100;
if (sumAllCents < 0)
{
finalDollars = -finalDollars;
finalCents = -finalCents;
}
return Money(finalDollars, finalCents);
}
//Uses cstdlib:
const Money operator -(const Money& amount1, const Money& amount2)
{
int allCents1 = amount1.cents + amount1.dollars*100;
int allCents2 = amount2.cents + amount2.dollars*100;
int diffAllCents = allCents1 - allCents2;
int absAllCents = abs(diffAllCents);
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
int finalDollars = absAllCents/100;
int finalCents = absAllCents%100;
if (diffAllCents < 0)
{
finalDollars = -finalDollars;
finalCents = -finalCents;
}
return Money(finalDollars, finalCents);
}
bool operator ==(const Money& amount1, const Money& amount2)
{
return
((amount1.dollars == amount2.dollars)
&& (amount1.cents == amount2.cents));
}
const Money operator -(const Money& amount)
{
return Money(-amount.dollars, -amount.cents);
}
Money::Money( ): dollars(0), cents(0)
{/*Body intentionally empty.*/}
Money::Money(double amount) : dollars(dollarsPart(amount)),
cents(centsPart(amount))
{/*Body intentionally empty*/}
Money::Money(int theDollars)
: dollars(theDollars), cents(0)
{/*Body intentionally empty*/}
//Uses cstdlib:
Money::Money(int theDollars, int theCents)
{
Page 17
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 18
if ((theDollars < 0 && theCents > 0) ||
(theDollars > 0 && theCents < 0))
{
cout << "Inconsistent money data.\n";
exit(1);
}
dollars = theDollars;
cents = theCents;
}
double Money::getAmount( ) const
{
return (dollars + cents*0.01);
}
int Money::getDollars( ) const
{
return dollars;
}
int Money::getCents( ) const
{
return cents;
}
int Money::dollarsPart(double amount) const
{
return static_cast<int>(amount);
}
int Money::centsPart(double amount) const
{
double doubleCents = amount*100;
int intCents = (round(fabs(doubleCents)))%100; //% can misbehave
//for negatives
if (amount < 0)
intCents = -intCents;
return intCents;
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 19
}
int Money::round(double number) const
{
return floor(number + 0.5);
}
ostream& operator <<(ostream& outputStream, const Money& amount)
{
int absDollars = abs(amount.dollars);
int absCents = abs(amount.cents);
if (amount.dollars < 0 || amount.cents < 0)
//accounts for dollars == 0 or cents == 0
outputStream << "$-";
else
outputStream << '$';
outputStream << absDollars;
if (absCents >= 10)
outputStream << '.' << absCents;
else
outputStream << '.' << '0' << absCents;
return outputStream;
}
//Uses iostream and cstdlib:
istream& operator >>(istream& inputStream, Money& amount)
{
char dollarSign;
inputStream >> dollarSign; //hopefully
if (dollarSign != '$')
{
cout << "No dollar sign in Money input.\n";
exit(1);
}
double amountAsDouble;
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 20
inputStream >> amountAsDouble;
amount.dollars = amount.dollarsPart(amountAsDouble);
amount.cents = amount.centsPart(amountAsDouble);
return inputStream;
}
Enter an amount of money:$95.85
Enter a percent you wish to take of your amount 17
Your amount is $95.85
My amount is $10.09
17% of your amount is $16.29
15% of my amount is $1.51
One of us is richer.
$95.85 + $10.09 equals $105.94
$95.85 - $10.09 equals $85.76
Your amount is $95.85
My amount is $10.09
Your amount is greater than or equal to my Amount
The sum of our Amounts is greater than the difference.
Your amount is greater than my Amount
The sum of our Amounts is greater than the difference.
2. Rational number class
This class implements rational number of the type 2/3.
Requirements:
Write a class Rational;
The private data consists of: int n, (numerator of the fraction) and int d (denominator of
the fraction).
The public interface consists of:
Constructors that have
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 21
two int args, to allow setting rational to any legitimate value
one int arg, to construct rationals with arg numerator and
denominator 1.
Overloaded operators << and >> to allow writing to screen in form 325/430
and reading from the keyboard in the same format.
Overloaded operators + - * / < <= > >= ==
Notes: either n or d, or both may contain a negative integer.
Put definitions in separate file for separate compilation
Test program required.
// file rational.h
#ifndef _RATIONAL_H_
#define _RATIONAL_H_
class Rational
{
public:
Rational( int numerator, int denominator );
Rational( int numerator ); // sets denominator to 1
Rational(); // sets numerator to 0, denominator to 1
friend Rational operator + ( const Rational&, const Rational& );
friend Rational operator - ( const Rational&, const Rational& );
friend Rational operator * ( const Rational&, const Rational& );
friend Rational operator / ( const Rational&, const Rational& );
friend bool operator < ( const Rational&, const Rational& );
friend bool operator <= ( const Rational&, const Rational& );
friend bool operator > ( const Rational&, const Rational& );
friend bool operator >= ( const Rational&, const Rational& );
friend bool operator == ( const Rational&, const Rational& );
friend ostream& operator << ( ostream&,
const Rational& );
friend istream& operator >> ( istream&, Rational& );
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 22
private:
int n;
int d;
};
void normalize(int &n, int &d);
#endif
//end file Rational.h
//file: ch8prb5.cc
//Implementations of the members of class Rational.
//For Chapter 8 Problem 5
#include <iostream>
#include <cstdlib>
#include "rational.h"
using namespace std;
//private members of class Rational
//
int n;
//
int d;
Rational::Rational( int numer, int denom )
{
normalize(numer, denom);
n = numer;
d = denom;
}
//sets denominator to 1
Rational::Rational( int numer ): n(numer), d(1) // See Text, Appendix 7
{
//body deliberately empty
}
// sets numerator to 0, denominator to 1
Rational::Rational():n(0), d(1) // see Appendix 7 of text
{
//body deliberately empty
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 23
}
Rational operator + ( const Rational& left, const Rational& right )
{
int numer = left.n * right.d + left.d * right.n;
int denom = left.d * right.d;
normalize(numer, denom);
Rational local(numer, denom);
return local;
}
Rational operator - ( const Rational& left, const Rational& right )
{
int numer = left.n * right.d - left.d * right.n;
int denom = left.d * right.d;
normalize(numer, denom);
Rational local (numer, denom);
return local;
}
Rational operator * ( const Rational& left, const Rational& right )
{
Rational product;
int numer = left.n * right.n;
int denom = left.d * right.d;
normalize(numer, denom);
product = Rational(numer, denom);
return product;
}
Rational operator / ( const Rational& left, const Rational& right )
{
Rational quotient;
int numer = left.n * right.d;
int denom = left.d * right.n;
normalize(numer, denom);
quotient = Rational(numer, denom);
return quotient;
}
//precondition: all relational operators require
d > 0
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 24
bool operator < ( const Rational& left, const Rational& right )
{
return left.n * right.d < right.n * left.d;
}
bool operator <= ( const Rational& left, const Rational& right )
{
return left.n * right.d <= right.n * left.d;
}
bool operator > ( const Rational& left, const Rational& right )
{
return left.n * right.d > right.n * left.d;
}
bool operator >= ( const Rational& left, const Rational& right )
{
return left.n * right.d >= right.n * left.d;
}
bool operator == ( const Rational& left, const Rational& right )
{
return left.n * right.d == right.n * left.d;
}
//NOTE:
//Doing input changes the input stream state. This seems obvious, but I
//have students who didn’t realize this.
//This code, along with iostream library, goes into an infinite loop if
//you make istream a const reference. There are no error messages, only
//an infinite loop, involving the single parameter constructor. This
can //be quite disconcerting to the naive student.
//
//Bottom line: The first param MUST NOT be const. The second one is
//written TO, so it cannot be const be either!
istream& operator >>( istream& in_str, Rational& right )
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 25
{
char ch;
in_str >> right.n >> ch >> right.d;
if (ch != '/')
{
// properly done, we would set iostream state
// to fail here in case of error.
cout << "bad input format for operator >>. Aborting!" << endl;
exit (1);
}
normalize(right.n, right.d);
return in_str;
}
//This, like the above case, along with g++ iostream library, goes into
//an infinite loop when you attempt to make ostream a const reference.
//There are no error messages, only an infinite loop, involving the
//single parameter constructor.
//
//Bottom line: The first parameter should not be const, the second is
//read only and should be const.
ostream& operator << ( ostream& out_str, const Rational& right )
{
char ch;
out_str << right.n << '/' << right.d;
return out_str;
}
//postcondition: return value is gcd of the absolute values of m and n
//depends on function int abs(int); declared in cstdlib
int gcd ( int m, int n)
{
int t;
m = abs (m);
// don't care about signs.
n = abs (n);
if ( n < m )
{
// make m >= n so algorithm will work!
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 26
t = m;
m = n;
n = t;
}
int r;
r = m % n;
while ( r != 0 )
{
r = m%n;
m = n;
n = r;
}
return m;
}
//postcondition: n and d (to be numerator and denominator of a
fraction)
//have all common factors removed, and d > 0.
void normalize( int& n, int& d)
{
// remove common factors:
int g = gcd( n, d);
n = n/g;
d = d/g;
//fix things so that if the fraction is 'negative'
//it is n that carries the sign. If both n and d are
//negative, each is made positive.
if ( n > 0 && d < 0 || n < 0 && d < 0 )
{
n = -n;
d = -d;
}
// assert:
d > 0
}
//end file ch8prb5.cc
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 27
// File: ch8prb5.tst.cc
// File: test program for Rational class
#include <iostream>
#include "rational.h"
using namespace std;
int main()
{
cout << "Testing declarations" << endl;
cout << "Rational x, y(2), z(-5,-6), w(1,-3);" << endl;
Rational x, y(2), z(-5,-6), w(1,-3);
cout
<< "z = " << z << ", y = " << y << ",
z = " << z
<< ", w = " << w << endl;
cout
<< "Testing >> overloading: \nEnter a fraction in the format "
<< "integer_numerator/integer_denominator" << endl;
cin >> x;
cout << "You entered the equivalent of: " << x << endl;
cout << z << " -
(" << w << ") = " << z - w << endl;
cout << "Testing the constructor and normalization routines: " <<
endl;
y =Rational(-128, -48);
cout << "y =Rational(-128, -48) outputs as " << y << endl;
y =Rational(-128, 48);
cout << "y =Rational(-128, 48 ) outputs as " << y << endl;
y = Rational(128,-48);
cout << "y = Rational(128, -48) outputs as " << y << endl;
Rational a(1,1);
cout << "Rational a(1,1); a outputs as: " << a << endl;
Rational ww = y*a;
cout <<
y << " * " << a << " = " << ww << endl;
w = Rational(25,9);
z = Rational(3,5);
cout
<< "Testing arithmetic and relational operator overloading"
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
<< endl;
cout << w << " * " << z << " = " << w * z << endl;
cout << w << " + " << z << " = " << w + z << endl;
cout << w << " - " << z << " = " << w - z << endl;
cout << w << " / " << z << " = " << w / z << endl;
cout << w << " <
" << z << " = " << (w < z) << endl;
cout << w << " < " << w << " = " << (w < w) << endl;
cout << w << " <= " << z << " = " << (w <= z) << endl;
cout << w << " <= " << w << " = " << (w <= w) << endl;
cout << w << " >
" << z << " = " << (w > z) << endl;
cout << w << " > " << w << " = " << (w > w) << endl;
cout << w << " >= " << z << " = " << (w >= z) << endl;
cout << w << " >= " << w << " = " << (w >= w) << endl;
w = Rational(-21,9);
z = Rational(3,5);
cout << w << " * " << z << " = " << w * z << endl;
cout << w << " + " << z << " = " << w + z << endl;
cout << w << " - " << z << " = " << w - z << endl;
cout << w << " / " << z << " = " << w / z << endl;
cout << w << " <
" << z << " = " << (w < z) << endl;
cout << w << " < " << w << " = " << (w < w) << endl;
cout << w << " <= " << z << " = " << (w <= z) << endl;
cout << w << " <= " << w << " = " << (w <= w) << endl;
cout << w << " >
" << z << " = " << (w > z) << endl;
cout << w << " > " << w << " = " << (w > w) << endl;
cout << w << " >= " << z << " = " << (w >= z) << endl;
cout << w << " >= " << w << " = " << (w >= w) << endl;
return 0;
}
// end file ch8prb5.tst.cc
A typical run follows
21:08:22:~/AW$ rational > rational.out
Page 28
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 29
45/35
21:08:34:~/AW$ cat rational.out
Testing declarations
Rational x, y(2), z(-5,-6), w(1,-3);
z = 5/6, y = 2/1,
z = 5/6, w = -1/3
Testing << overloading:
Enter a fraction in the format integer_numerator/integer_denominator
You entered the equivalent of: 9/7
5/6 -
(-1/3) = 7/6
Testing the constructor and normalization routines:
y =Rational(-128, -48) outputs as 8/3
y =Rational(-128, 48 ) outputs as -8/3
y =Rational(128, -48) outputs as -8/3
Rational a(1,1); a outputs as: 1/1
-8/3 * 1/1 = -8/3
Testing arithmetic and relational operator overloading
25/9 * 3/5 = 5/3
25/9 + 3/5 = 152/45
25/9 - 3/5 = 98/45
25/9 / 3/5 = 125/27
25/9 <
3/5 = 0
25/9 < 25/9 = 0
25/9 <= 3/5 = 0
25/9 <= 25/9 = 1
25/9 >
3/5 = 1
25/9 > 25/9 = 0
25/9 >= 3/5 = 1
25/9 >= 25/9 = 1
-7/3 * 3/5 = -7/5
-7/3 + 3/5 = -26/15
-7/3 - 3/5 = -44/15
-7/3 / 3/5 = -35/9
-7/3 <
3/5 = 1
-7/3 < -7/3 = 0
-7/3 <= 3/5 = 1
-7/3 <= -7/3 = 1
-7/3 >
3/5 = 0
-7/3 > -7/3 = 0
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 30
-7/3 >= 3/5 = 0
-7/3 >= -7/3 = 1
3. A Complex number class
Chapter 8, problem 3: Define an ADT for complex numbers. The problem specifies a
form of a + i*b where a and b are of type double, and i is the complex unit,
square root of -1. Implement operator overloading for ==, +, -, <, *, >>, and <<.
I have implemented a bit more than is required. The problem is to produce an ADT for
complex numbers. The intention is not to produce a complex class that is particularly
usable. Rather the intent is to produce a class that is reasonably close to complete yet still
understandable to the student. Even so, I acknowledge some overkill for the problem as
specified in the text.
I have used an external form different from this specification. The ANSI C++ (draft)
Standard says that the complex inserter and extractor should read and write a complex
numbers of the form: re, (re), or (re, im), where re is the real part and im is the
imaginary part. This is the external form required by the ISO Standard for compliant
compilers.
I am only allowing the external form (re, im). I check only the input format, and I do not
check for a good stream state at each read from the input stream. Robust software
requires checking the stream state at each fetch.
The student should not be expected to have a knowledge of the requirements of the ISO
C++ Standard. Before assigning this exercise, the instructor should provide the student
with information about the external form of a complex that the C++ Standard expects.
If the instructor prefers the external form as the text suggests, the i/o routines are the only
ones that are affected by this change.
Not coincidentally, the ISO Standard requires a fully implemented <complex> type with
overloaded operators, transcendental functions, a complex arrays including array slices,
as part of the Numerics library.
//file: complex.h
// Chapter 8, problem 6: Define an ADT for complex numbers.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 31
#ifndef _COMPLEX_H
#define _COMPLEX_H
#include <cmath>
#include <iostream>
using namespace std;
class complex
{
public:
complex (double r = 0, double i = 0): re (r), im (i) { }
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend double real (const complex&) ;
friend double imag (const complex&)
;
friend complex operator + (const complex&, const complex&);
friend complex operator - (const complex&, const complex&);
friend complex operator * (const complex&, const complex&);
friend complex operator / (const complex&, const complex&);
friend bool operator == (const complex&, const complex&);
friend bool operator != (const complex&, const complex&);
friend complex polar (double, double)
;
friend istream& operator>> (istream&, complex&);
friend ostream& operator<< (ostream&, const complex&);
};
double norm (const complex& x);
#endif
//file: complex.cxx
//This is the implementation file for members of the class complex.
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 32
//The class interface is given in complex.h
#include "complex.h"
#include <iostream>
ostream& operator<< (ostream& o, const complex& c)
{
o << "(" << c.re << ", " << c.im << ")";
return o;
}
//only limited checking of format is done here.
istream& operator>> (istream& ins, complex& z)
{
double r, i;
char ch;
ins >> ch;
if ('(' != ch) //if the complex number isn't in required form,
//complain and exit.
{
cout << "\nBad complex form: found
"
<< ch << ", need ( for complex input; \n"
<< "A complex must be of the form (re, im) \n";
exit(1);
}
//We have '(' -- now get the real part
ins >> r;
//and get the comma
ins >>ch;
if (',' != ch ) //complex number must have a comma next,
//if not, complain and exit.
{
cout << "\nBad complex form: found
"
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
<< ch << ", need comma for complex input; \n"
<< "A complex must be of the form (re, im) \n";
exit(1);
}
//now get the imaginary part
ins >> i;
//and get the close parenthesis
ins >> ch;
if ( ')' != ch ) //complex number must have a ')' last,
//If not, complain and exit.
{
cout << "\nBad complex form: found
"
<< ch << ", need ) for complex input; \n"
<< "A complex must be of the form (re, im) \n";
exit(1);
}
z = complex(r,i);
return ins;
}
double imag (const complex& x)
{
return x.imag ();
}
double real (const complex& x)
{
return x.real ();
}
complex operator + (const complex& x, const complex& y)
{
Page 33
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
return complex (real (x) + real (y), imag (x) + imag (y));
}
complex operator - (const complex& x, const complex& y)
{
return complex (real (x) - real (y), imag (x) - imag (y));
}
complex operator * (const complex& x, const complex& y)
{
return complex (real (x) * real (y) - imag (x) * imag (y),
real (x) * imag (y) + imag (x) * real (y));
}
complex operator / (const complex& x, double y)
{
return complex (real (x) / y, imag (x) / y);
}
bool operator == (const complex& x, const complex& y)
{
return real (x) == real (y) && imag (x) == imag (y);
}
bool operator != (const complex& x, const complex& y)
{
return real (x) != real (y) || imag (x) != imag (y);
}
double abs (const complex& x)
{
return sqrt ( norm(x) );
}
complex conj (const complex& x)
{
return complex (real (x), -imag (x));
}
Page 34
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 35
double norm (const complex& x)
{
return real (x) * real (x) + imag (x) * imag (x);
}
//Divide overloading: There is a possible bug here. The usual tool for
//complex division, num/den = num*conj(den)*(1/(den * conj(den)),
//causes an infinite recursion.
//Exercise: How and why?
complex operator / (const complex& num, const complex& den)
{
return ( num * conj(den) * (1/norm(den) ));
}
//file: tstcmplx.cxx
//To test complex.h and complex.cxx class, members and friends
#include "complex.h"
#include "cmath"
using namespace std;
//compile command: g++ testcomplex.cc complex-io.cc
int main()
{
// test constructors
complex x, y(3),
cout <<"x =
z(-3.2, 2.1);
" << x << " y = " << y << " z = " << z << endl << endl;
x = complex(3, -4);
cout << "testing members and support functions as well as”
<< " output operator:\n"
<< "complex number x = " << x << endl
<< "real part: " << x.real() << endl
<< "real part from friend real(x): " << real(x) << endl
<< "imaginary part: " << x.imag() << endl
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 36
<< "imaginary part from friend imag(x) : " << imag(x) << endl
<< "norm: " << norm(x) << endl << endl;
cout << "test complex arithmetic and output routines: \n\n";
y = complex (1, -1);
cout <<"x =
" << x << " y = " << y << " z = " << z << endl << endl;
z = x + y;
cout <<"z = x + y = " << z << endl;
z = x * y;
cout <<"z = x * y = " << z << endl;
z = x - y;
cout <<"z = x - y = " << z << endl;
z = x / y;
cout <<"z = x / y = " << z << endl << endl;
//test of automatic conversion double -> complex by the
//constructor.
double d(2.0);
cout << "d: " << d << "
cout << "x+d: " ;
z = x + d;
cout << z << endl;
z = x - d;
cout << "x-d: " ;
cout << z << endl;
z = x * d;
cout << "x*d: " ;
cout << z << endl;
z = x / d;
cout << "x/d: " ;
cout << z << endl;
z = d + x;
cout << "d+x: " ;
x: " << x <<endl;
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
Page 37
cout << z << endl;
z = d - x;
cout << "d-x: " ;
cout << z << endl;
z = d * x;
cout << "d*x: " ;;
cout << z << endl;
z = d / x;
cout << "d/x: " ;;
cout << z << endl;
//test whether double/complex and complex/complex give same result:
complex two(2,0);
cout << "two/x: ";
cout << two/x << endl;
cout << "\nGetting data from standard input: \n";
cin >> x >> y;
cout <<"data read is: x = " << x << " y = " << y << endl << endl;
return 0;
}
Test data and a test run:
$cat good-data
(1,1)
(2,3)
Test run:
$a.out < good-data
x =
(0, 0) y = (3, 0) z = (-3.2, 2.1)
testing members and support functions as well as output operator:
complex number x = (3, -4)
real part: 3
real part from friend real(x): 3
imaginary part: -4
imaginary part from friend imag(x) : -4
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
norm: 25
test complex arithmetic and output routines:
x =
(3, -4) y = (1, -1) z = (-3.2, 2.1)
z = x + y = (4, -5)
z = x * y = (-1, -7)
z = x - y = (2, -3)
z = x / y = (3.5, -0.5)
d: 2
x: (3, -4)
x+d: (5, -4)
x-d: (1, -4)
x*d: (6, -8)
x/d: (1.5, -2)
d+x: (5, -4)
d-x: (-1, 4)
d*x: (6, -8)
d/x: (0.24, 0.32)
two/x: (0.24, 0.32)
Getting data from standard input:
data read is: x = (1, 1) y = (2, 3)
Some test of the input routine with bad data:
$cat bad-data1
(1 2)
$a.out < bad-data1
[...snip...]
Getting data from standard input:
Bad complex form: found
2, need comma for complex input;
Page 38
Instructor’s Resource Manual for Savitch Absolute C++ 02/16/16
Chapter 8 Operator Overloading, Friends, and References
A complex must be of the form (re, im)
$cat bad-data2
(1,2)
(1,2_
$a.out < bad-data2
[...snip...]
Getting data from standard input:
Bad complex form: found
_, need ) for complex input;
A complex must be of the form (re, im)
4. Cumulative modification of Display 8.7
I omitted this problem.
Page 39
Download