Você está na página 1de 52

1

Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley


Chapter 11
Friends and Overloaded Operators
1. Solutions to and Remarks on Selected Programming Problems
1. Modify Money class.
No solution is provided for this exercise.
2. Implement constructors and overload >>, <<, +, and * for class Pair.
//Chapter 11 Programming Problem #2
//Enhance Self Test Problem 17 by completing the
// overloading of operator<< and >> for class Pair
// Implement:
// The default constructor
// A one int parameter constructor that
// sets the first member to the parameter
// and the second to 0.
// A two int parameter constructor.
// Overload * on a Pair and an int
// (x, y)*c = (x*c, y*c)
// Overload + on two objects of type Pair.
// (a, b) + (c, d) = (a + c, b + d)

#include <iostream>
using namespace std;

class Pair
{
public:
Pair();
Pair(int first, int second);
Pair(int first);

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

2
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
int accessFirst();
int accessSecond();
// other members and friends
friend Pair operator+(const Pair&, const Pair&);
friend Pair operator*(const Pair&, int);

friend istream& operator>> (istream&, Pair&);
friend ostream& operator<< (ostream&, const Pair&);

private:
int f;
int s;
};

int main()
{
Pair x;
Pair y(2,3);
Pair z(4);

cout << x.accessFirst()<< endl;
cout << x.accessSecond()<< endl;
cout << y.accessFirst()<< endl;
cout << y.accessSecond()<< endl;
cout << z.accessFirst()<< endl;
cout << z.accessSecond()<< endl;
cout << endl;
cout << "x " << x << endl
<< "y " << y << endl
<< "z " << z
<< endl << endl;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

3
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

x = y + z;
cout << "y + z " << x << endl;
cout << "y * 2 " << y * 2 << endl;
return 0;
}

Pair operator+(const Pair& lhs, const Pair& rhs)
{
return Pair(lhs.f + rhs.f, lhs.s + rhs.s);
}
Pair operator*(const Pair& lhs, int rhs)
{
return Pair(lhs.f * rhs, lhs.s * rhs);
}

istream& operator>> (istream& ins, Pair& second)
{
char ch;
ins >> ch; // discard init '('
ins >> second.f;
ins >> ch; // discard comma ','
ins >> second.s;
ins >> ch; // discard final '('
return ins;
}

ostream& operator<< (ostream& outs, const Pair& second)
{
outs << '(';
outs << second.f;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

4
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
outs << ", " ;// I followed the Author's suggestion here.
outs << second.s;
outs << ")";
return outs;
}

Pair::Pair(int firstValue, int secondValue)
:f(firstValue), s(secondValue)
{// deliberately empty
}

Pair::Pair(int firstValue)
:f(firstValue), s(0)
{// deliberately empty
}

Pair::Pair(): f(0), s(0)
{// deliberately empty
}

int Pair::accessFirst(){ return f; }
int Pair::accessSecond(){ return s; }
Sample run
0
0
2
3
4
0

x (0, 0)
y (2, 3)
z (4, 0)

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

5
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
y + z (6, 3)
y * 2 (4, 6)

34. No Solutions Provided

5. Rational Number Class
This class implements rational number of the type 2/3.
Requirements:
class Rational;

private data: int n, (fraction numerator) and int d
(fraction denominator).

public interface:
constructors:
two int args, to allow setting rational to any legal
value
one int arg, to construct rationals with arg numerator
and denominator 1.
overload << and >> to allow writing to screen in form
325/430
and reading it from the keyboard in the same format.

Notes: either n or d may contain a negative quantity.
overload + - * / < <= > >= ==
Put definitions in separate file for separate compilation

Test program required.

// file rational.h
#ifndef RATIONAL_H_
#define RATIONAL_H_

class Rational
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

6
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
{
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&);
private:
int n;
int d;
};
void normalize(int &n, int &d);

#endif
//end file Rational.h
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

7
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

//file: ch11prb4cc
//Implementations of the members of class Rational.
//For Chapter 11 Problem 4

#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 the initializer appendix
{
//body deliberately empty
}

// sets numerator to 0, denominator to 1
Rational::Rational():n(0), d(1)
// see initializer appendix
{
//body deliberately empty
}

Rational operator +(const Rational& left,
const Rational& right)
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

8
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
{
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;
}
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

9
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

//precondition: all relational operators require d > 0
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.

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

10
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
//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, so it cannot be const either.

istream& operator >>(istream& in_str, Rational& right)
{
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)
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

11
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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!
{
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)
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

12
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
// 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 ch11prb4.cc

//File: ch11prb4.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;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

13
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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" << 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;

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

14
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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 ch11prb4.tst.cc

A typical run follows

21:08:22:~/AW$ rational > rational.out
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
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

15
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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
-7/3 >= 3/5 = 0
-7/3 >= -7/3 = 1
6. Complex Numbers
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 complete yet still
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

16
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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 ISO/ANSI C++
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 that the C++ compilers will 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 the C++ Standard expects.
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 11, problem 5: Define an ADT for complex numbers.

#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; }
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

17
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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.cpp
//This is the implementation file for members of the
//class complex. The class interface is given in complex.h

#include "complex.h"
#include <iostream>

ostream& operator<< (ostream& o, const complex& c)
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

18
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
o << "(" << c.re << ", " << c.im << ")";
return o;
}

//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 "
<< ch << ", need comma for complex input; \n"
<< "A complex must be of the form (re, im) \n";
exit(1);
}

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

19
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
//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)
{
return complex (real (x) + real (y), imag (x) + imag (y));
}

complex operator - (const complex& x, const complex& y)
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

20
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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));
}

double norm (const complex& x)
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

21
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
{
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.cpp
//To test complex.h and complex.cpp class, members and
//friends
#include "complex.h"
#include "cmath"
using namespace std;

//compile command: g++ testcomplex.cpp complex-io.cpp

int main()
{
// test constructors
complex x, y(3), z(-3.2, 2.1);
cout <<"x = " << x << " y = " << y
<< " z = " << z << endl << endl;

x = complex(3, -4);

cout << "testing members and support functions as "
<< " well as output operator:\n"
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

22
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
<< "complex number x = " << x << endl
<< "real part: " << x.real() << endl
<< "real part from friend real(x): "
<< real(x) << endl
<< "imaginary part: " << x.imag() << endl
<< "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 << " x: " << x <<endl;
cout << "x+d: " ;
z = x + d;
cout << z << endl;
z = x - d;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

23
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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: " ;
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:
The content of the file, good-data, is:
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

24
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
(1,1)
(2,3)
Here is the output from our input routine when given bad 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
norm: 25

We 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:
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

25
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
data read is: x = (1, 1) y = (2, 3)
Messages from the input routine when given bad data.
The content of the file, bad-data1, is:
(1 2)
The messages from the input routine with this file for data follow.
[...snip...]

Getting data from standard input:

Bad complex form: found 2, need comma for complex input;
A complex must be of the form (re, im)

The content of the file, bad-data2, is:
(1,2)
(1,2_
The messages from the input routine with this file for data follow.
[...snip...]
Getting data from standard input:

Bad complex form: found _, need )for complex input;
A complex must be of the form (re, im)

7. No Solution Provided
8. No Solution Provided
9. No Solution Provided
10. No Solution Provided
11. No Solution Provided
12.

// ****************************************************************
//
// Ch11Proj12.cpp
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

26
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
//
// This program defines a class for storing a set of STL strings.
// The + operator unions two sets and the * operator intersects
// two sets.
//
// ****************************************************************

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>

using namespace std;

class StringSet
{
public:
StringSet();
StringSet(const string initialStrings[], int arraysize);
bool add(const string s);
bool remove(const string s);
void clear();
int size();
void output();
friend StringSet operator *(const StringSet &set1, const
StringSet &set2);
friend StringSet operator +(const StringSet &set1, const
StringSet &set2);
private:
int search(const string s);
vector<string> data;
};


// ======================
// StringSet::StringSet
// Default Constructor
// ======================
StringSet::StringSet()
{
}

// ======================
// StringSet::StringSet
// This constructor initializes the string set to those
// strings in the input array.
// ======================
StringSet::StringSet(const string initialStrings[], int arraysize)
{
int i;
for (i=0; i < arraysize; i++)
{
data.push_back(initialStrings[i]);
}
}

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

27
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
// ======================
// StringSet::Output
// This method simply outputs all strings to the console.
// ======================
void StringSet::output()
{
int i;
for (i=0; i < data.size(); i++)
{
cout << " " << data[i] << endl;
}
}

// ======================
// StringSet::clear
// Erases all entries in the stringset.
// ======================
void StringSet::clear()
{
data.clear(); // Erases all elements from the vector
}

// ======================
// StringSet::size
// Number of entries in the stringset.
// ======================
int StringSet::size()
{
return data.size();
}

// ======================
// StringSet::search
// This private member function searches the vector for
// the target string. If found, the index is returned,
// otherwise -1 is returned.
// ======================
int StringSet::search(const string s)
{
int i;
for (i=0; i < data.size(); i++)
{
if (data[i]==s) return i;
}
return -1;
}

// ======================
// StringSet::add
// Adds a new entry to the vector.
// If the entry already exists, then "false" is returned,
// otherwise "true" is returned.
// ======================
bool StringSet::add(const string s)
{
int i;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

28
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

i = search(s);
if (i>=0)
{
return false; // s already in the set
}
data.push_back(s);
return true;
}

// ======================
// StringSet::remove
// Removes an entry from the vector.
// If the entry doesnt exist, then "false" is returned,
// otherwise "true" is returned and the string is removed.
// ======================
bool StringSet::remove(const string s)
{
int i;

i = search(s);
if (i>=0)
{
// To remove the string from the vector, we make a new
// copy of the vector without the string, then copy that
// back to the original vector.
// Chapter 18 describes a better technique using the
"erase"
// method and iterators.
vector<string> temp;
for (int j=0; j<data.size(); j++)
{
// Copy all items that are not the target to delete
if (data[j] != s)
{
temp.push_back(data[j]);
}
}
// Clear original vector
data.clear();
// Copy temp to original
for (int j=0; j<temp.size(); j++)
{
data.push_back(temp[j]);
}
return true;
}
return false;
}

// ======================
// StringSet::operator *
// Intersects the current StringSet and otherSet, and returns
// a new StringSet that is the intersection of the two.
// This code loops and compares all strings in each and adds the
// common words to the new StringSet.
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

29
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
// ======================
StringSet operator *(const StringSet &set1, const StringSet &set2)
{
StringSet temp;
int i,j;

for (i=0; i<set1.data.size(); i++)
{
for (j=0; j<set2.data.size(); j++)
{
if (set1.data[i]==set2.data[j])
{
temp.add(set1.data[i]);
}
}
}
return temp;
}

// ======================
// StringSet::operator +
// Unions the current StringSet and otherSet, and returns
// a new StringSet that is the union of the two.
// This code adds all the strings from the first set to temp,
// then adds any words from the second set as well. The add
// function will weed out any duplicates.
// ======================
StringSet operator +(const StringSet &set1, const StringSet &set2)
{
StringSet temp;
int i;

// Add everything from set 1
for (i=0; i<set1.data.size(); i++)
{
temp.add(set1.data[i]);
}
// Add everything from set 2
for (i=0; i<set2.data.size(); i++)
{
temp.add(set2.data[i]);
}
return temp;
}

// ======================
// main function
// ======================
int main()
{
// Some simple StringSet tests
string initialStrings[] = {"foo","bar","zot"};
StringSet sset(initialStrings,3);

cout << "Initial set:" << endl;
sset.output();
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

30
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
cout << "Remove bar:" << endl;
sset.remove("bar");
sset.output();
cout << "Add alpha:" << endl;
sset.add("alpha");
sset.output();
cout << "Size of set is " << sset.size() << endl;

string initialStrings2[] = {"one","two","three","four"};
string initialStrings3[] = {"two","five","three"};

StringSet sset2(initialStrings2,4);
StringSet sset3(initialStrings3,3);

cout << endl;
cout << "Set 2:" << endl;
sset2.output();
cout << "Set 3: " << endl;
sset3.output();
cout << "Intersection of sets 2 and 3:" << endl;
StringSet sset4 = sset2 * sset3;
sset4.output();
cout << "Union of original set and previous set:" << endl;
StringSet sset5 = sset4 + sset;
sset5.output();

cout << "Clear set 4:" << endl;
sset4.clear();
sset4.output();
return 0;
}

13.

// ****************************************************************
//
// Ch11Proj13.cpp
//
// This program represents a document and query as a set of keywords.
// It uses the binary cosine metric to then compute the similarity
// between the query and document and outputs the similarity as a
// value between 0 and 1.
//
// This program uses the StringSet class from the previous project.
// ****************************************************************

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <string>
#include <cmath>
#include <fstream>

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

31
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
using namespace std;

// ******************************* Begin StringSet Class

class StringSet
{
public:
StringSet();
StringSet(const string initialStrings[], int arraysize);
bool add(const string s);
bool remove(const string s);
void clear();
int size();
void output();
friend StringSet operator *(const StringSet &set1, const
StringSet &set2);
friend StringSet operator +(const StringSet &set1, const
StringSet &set2);
private:
int search(const string s);
vector<string> data;
};


// ======================
// StringSet::StringSet
// Default Constructor
// ======================
StringSet::StringSet()
{
}

// ======================
// StringSet::StringSet
// This constructor initializes the string set to those
// strings in the input array.
// ======================
StringSet::StringSet(const string initialStrings[], int arraysize)
{
int i;
for (i=0; i < arraysize; i++)
{
data.push_back(initialStrings[i]);
}
}

// ======================
// StringSet::Output
// This method simply outputs all strings to the console.
// ======================
void StringSet::output()
{
int i;
for (i=0; i < data.size(); i++)
{
cout << " " << data[i] << endl;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

32
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
}
}

// ======================
// StringSet::clear
// Erases all entries in the stringset.
// ======================
void StringSet::clear()
{
data.clear(); // Erases all elements from the vector
}

// ======================
// StringSet::size
// Number of entries in the stringset.
// ======================
int StringSet::size()
{
return data.size();
}

// ======================
// StringSet::search
// This private member function searches the vector for
// the target string. If found, the index is returned,
// otherwise -1 is returned.
// ======================
int StringSet::search(const string s)
{
int i;
for (i=0; i < data.size(); i++)
{
if (data[i]==s) return i;
}
return -1;
}

// ======================
// StringSet::add
// Adds a new entry to the vector.
// If the entry already exists, then "false" is returned,
// otherwise "true" is returned.
// ======================
bool StringSet::add(const string s)
{
int i;

i = search(s);
if (i>=0)
{
return false; // s already in the set
}
data.push_back(s);
return true;
}

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

33
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
// ======================
// StringSet::remove
// Removes an entry from the vector.
// If the entry doesnt exist, then "false" is returned,
// otherwise "true" is returned and the string is removed.
// ======================
bool StringSet::remove(const string s)
{
int i;

i = search(s);
if (i>=0)
{
// To remove the string from the vector, we make a new
// copy of the vector without the string, then copy that
// back to the original vector.
// Chapter 18 describes a better technique using the
"erase"
// method and iterators.
vector<string> temp;
for (int j=0; j<data.size(); j++)
{
// Copy all items that are not the target to delete
if (data[j] != s)
{
temp.push_back(data[j]);
}
}
// Clear original vector
data.clear();
// Copy temp to original
for (int j=0; j<temp.size(); j++)
{
data.push_back(temp[j]);
}
return true;
}
return false;
}

// ======================
// StringSet::operator *
// Intersects the current StringSet and otherSet, and returns
// a new StringSet that is the intersection of the two.
// This code loops and compares all strings in each and adds the
// common words to the new StringSet.
// ======================
StringSet operator *(const StringSet &set1, const StringSet &set2)
{
StringSet temp;
int i,j;

for (i=0; i<set1.data.size(); i++)
{
for (j=0; j<set2.data.size(); j++)
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

34
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
if (set1.data[i]==set2.data[j])
{
temp.add(set1.data[i]);
}
}
}
return temp;
}

// ======================
// StringSet::operator +
// Unions the current StringSet and otherSet, and returns
// a new StringSet that is the union of the two.
// This code adds all the strings from the first set to temp,
// then adds any words from the second set as well. The add
// function will weed out any duplicates.
// ======================
StringSet operator +(const StringSet &set1, const StringSet &set2)
{
StringSet temp;
int i;

// Add everything from set 1
for (i=0; i<set1.data.size(); i++)
{
temp.add(set1.data[i]);
}
// Add everything from set 2
for (i=0; i<set2.data.size(); i++)
{
temp.add(set2.data[i]);
}
return temp;
}

// ******************************* End StringSet Class

// Function prototypes
void readFileKeywords(StringSet &setDocument, const char filename[]);
void inputKeywords(StringSet &setKeywords);


// ************************
// InputKeywords:
// Inputs strings from the keyboard
// and stores them into the set until
// enter is pressed.
// ************************
void inputKeywords(StringSet &setKeywords)
{
string s;
cout << "Enter keywords, one per line. Enter a blank line when
finished."
<< endl;
do
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

35
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
getline(cin,s);
if (s != string(""))
{
setKeywords.add(s);
}
} while (s != string(""));
}


// ************************
// ReadFileKeywords:
// Opens the specified file for reading and inputs its words
// into the StringSet.
// ************************
void readFileKeywords(StringSet &setDocument, const char filename[])
{
ifstream in_stream;
string s;

// Open the file
in_stream.open(filename);
if (in_stream.fail())
{
cout << "Input file opening failed." << endl;
exit(-1);
}
// Read each word from the file until there are no more
while (!in_stream.eof())
{
// Input word, ideally we should filter out
// punctuation and other non-letters but will skip now
// for purposes of this assignment
in_stream >> s;
setDocument.add(s);
}
in_stream.close();
}


// ======================
// main function
// ======================
int main()
{
// Variable declarations
StringSet doc1;
StringSet doc2;
StringSet query;

readFileKeywords(doc1, "Ch11Proj13Doc1.txt");
readFileKeywords(doc2, "Ch11Proj13Doc2.txt");
cout << "Enter set of keywords for the query." << endl;
inputKeywords(query);

// Calculate intersection
StringSet intersect1 = doc1 * query;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

36
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
StringSet intersect2 = doc2 * query;

// Calculate similarity to each
double sim1 = intersect1.size() / (sqrt(doc1.size()) *
sqrt(query.size()));
cout << "The similarity to document 1 is " << sim1 << endl;

double sim2 = intersect2.size() / (sqrt(doc2.size()) *
sqrt(query.size()));
cout << "The similarity to document 2 is " << sim2 << endl;

return 0;
}

chocolate ice cream and chocolate candy bars are my favorite things
to eat

I like to eat broccoli and fish along with tofu and brussel sprouts all
day long


2. Outline of Topics in the Chapter
11.1 Friend Function
An Equality Function
Friend Functions
Define Both Accessor Functions and Friend Functions
Use Both Member and Nonmember Functions
Money Class (Version 1)
Implementation of digit_to_int (Optional)
Leading Zeros in Number Constants
The const Parameter Modifier
Inconsistent Use of const
11.2 Overloading Operators
Overloading Operators
Constructors for Automatic Type Conversion
Overloading Unary Operators
Overloading >> and <<

3. General Comments on the Chapter
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

37
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Here many of the tools necessary for defining abstract data types are presented. The
notion of friend, the const keyword, operator overloading, user-defined casts, and
separate compilation are all important to the construction of Abstract Data types.
Namespaces are treated, since the standard libraries require them.
11.1 Friend Functions
This section starts by revisiting the class DayOfYear. Recall that few of the operations
(+, /, -, ==, <, etc.) apply to ADTs directly. We do have assignment, = ,
that results in member wise copy. This means the right hand objects members are each
assigned to the left-hand objects members. We will see later that frequently this should
be called member-UN-wise copy, since under circumstances frequently encountered, this
results in a disaster. (Thanks to John Gibson for the expression "member UN-wise
copy".)
While revisiting the DayOfYear class, the text implements == as an ordinary function,
bool equal(DayOfYear date1, DayOfYear date2);
The text points out that this requires access functions, but even access functions turns out
to provide insufficient (i.e., inefficient) access. We need friend access.
Friend Functions
Enter the notion "friend of a class". A friend of a class is a function that is not a member,
but the class grants the function access to all members of the class whether the members
are public or private. friend is a C++ reserved keyword.
A function is a friend of a class if that function is declared within the class, preceded by
the friend keyword. A friend function is as much a part of the interface of a class as a
member. The property of being a friend is granted by the class by the friend function
being declared
1
within the class of which it is a friend, prefixed with the keyword
friend. The class grants this access, so there is no violation of the protection afforded
by the private section of the class. The friend function is not a member of the class.

1
The ANSI/ISO C++ language refers to a function declaration where the C language refers to a
function prototype. This usage has found its way into earlier C++ texts. Earlier in this IRM, we
have used declaration (prototype). Since the author uses function declaration consistently, we
will use declaration in the sequel.
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

38
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Consequently, the friend function is not called with member access operators, unless it is
a member of some other class. The text does not discuss this.
We emphasize the class grants friendship. A function cannot unilaterally seize friendship
of a class. The declaration must be made in the class definition. It is pointed out in the
text that friend status can result in increased efficiency by allowing bypassing access
functions that are otherwise necessary.
A function that is a friend of a class, unless it is a function member of another class, will
be defined outside the scope of any class, making the function global, that is, accessible
in any function definition.
Like any programming effort, there is a balance to be struck between use of friend
functions and use of accessor functions. If a function needs to write to a private
member of a class, one can have an accessor return a reference to the private
member, but this makes the private member globally writeable. In this case, we
might as well make the variable public. (This is where you need a friend :).
2

Example:
//File: Friend.cc
//To illustrate return by reference.
#include <iostream>
using namespace std;

class F
{
public:
F();
//various constructors
int& rw_accessor_i();
friend int& rw_friend_accessor_i(F& w);
int read_accessor_i();
private:

2
The symbol :)is called a smiley. This was originally inserted into email to represent intent to
be amusing. There are many, many of these. See Smileys, David Sanderson, OReilly, 1993.
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

39
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
int i;
};

F::F() { i = 10; } //we could have written F::F():i(10) {}

int F::read_accessor_i(){ return i; }

int& F::rw_accessor_i()
{
return i; // not &i, which is the address of i.
}
The argument in the following function must be pass-by-reference otherwise only the
local variable x is changed when the variable that is returned is assigned, and the caller's
argument is not changed. This is a classic error. There must be a chain of references from
the returned value to the caller's argument.
int& rw_friend_accessor_i(F& x)
{
return x.i; //
}

int main()
{
F x;
cout << "x.read_accessor_i() gives "
<< x.read_accessor_i()<< endl;
cout << "x.rw_accessor_i() gives "
<< x.rw_accessor_i() << endl << endl;

x.rw_accessor_i() = 0; //note function call is an l-value

cout << "After assignment with 0, "
<<" x.read_accessor_i() gives "
<< x.read_accessor_i()<< endl;
cout << "x.rw_accessor_i() gives "
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

40
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
<< x.rw_accessor_i() << endl << endl;

rw_friend_accessor_i(x) = 14;
cout << "After assigning rw_friend_accessor_i(x) "
<< " with 14, x.read_accessor_i() gives: "
<< x.read_accessor_i() << endl;

return 0;
}
Debugging Notes
I chose to type in the money ADT in Display 11.3 rather than download from the
Addison Wesley web site, www.aw.com/savitch, I found that I made two typographical
errors worth a remark.
Some errors in typing do indeed compile and run to a successful if not correct conclusion.
In the routine Money::output(ostream&), instead of
positive_cents = labs(all_cents);
I had
positive_cents - labs(all_cents);
I had typed '-' instead of a '='. This expression is evaluated and the value is
ignored. This adjacent-key typo caused interesting output. The error was caused by never
setting positive_cents. While this error is evident from a quick look at the code,
there are typos of this kind that are very hard to find, hence are worth mentioning to the
students.
The - for = error produces this output:
enter an amount of money:$10.09
Your amount is $6070067.52
My amount is $25.92
We have the same amount.
$245.84 + $246.08 equals $246.08
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

41
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
This suggests that the error is in the input or early in the output routines, since
my_amount is set by constructor. The value reported for my_amount is wrong, as
well as a ridiculous result for your_amount.
Placing output statements in Money::input determined that this routine was working
correctly. As soon as I looked at the output routine the lack of an = sign in the second
line jumped out at me. The errors in output for my_amount and your_amount are
caused by not setting positive_cents in that routine.
The other error was leaving out the last two lines of Money::input(istream&).
The program worked fine until tested with negative values, which were accepted, and
recorded as positive entries. These seem like trivial errors, but my students make errors
without having any idea about what the possibilities are, nor how to test to find the errors,
let alone how to do efficient testing.
A debugger can be a great help but it is not a panacea. Frequently it is faster to insert
output statements at places in the code that are suggested by the errors produced. Under
any circumstances, whether a debugger is used or output statements are added, planning
is necessary.
Again, I point out the need for the student to create her own exercises like those in
Chapter 1 of the text that help students see what error messages are issued for particular
questions about syntax.
Many errors are more subtle. Fixing the typo fixed the routine, until I tried it for negative
money amounts, as noted above.
The const Parameter Modifier
Remember these three things about usage of the const keyword. One: Use of the
keyword const is a promise to the compiler that you wont write code that changes
something. Two: This use is a request that the compiler enforce these promises. Three:
The use of const is difficult or impossible to retrofit. If you write code without const
correctness, that code almost certainly will have to be rewritten from the ground up to get
the code right.
The declaration
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

42
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
const int x = 3;
is a promise to the compiler that you wont write anything that has the potential to change
x, such as using x on the left of an assignment statement or as an argument to a non-
const reference parameter. A const member function promises not to change any class
data using that function. A use of const in a class object declaration promises that you
wont call any function with the potential to change that objects data members. As
mentioned, the compiler tries to enforce these promises.
The text points out that use of the const keyword requires consistency. If you declare a
const parameter for a function member declaration in a class, you must use the const
keyword in your definition of that function member. If you use a non-const object for
an argument, and do not declare the function with a const parameter, you should get an
error or a warning from your compiler. If you fail to declare a member function to be
const, then call this member on behalf of a const object of this class, as below, you
should get an error. The reason is in C++ the compilers encode all the information about
a function: name of the function, the sequence of types in the argument list, including
const qualifiers, and for member functions of a class, any const qualifiers that follow the
declaration and uses this as the function name. If the const qualifier is in the definition
but not in the class declaration, as far as the compiler is concerned, they are different
functions, and you get an error.
It is worth noting that in the following example, the Borland compiler is more forgiving
of the const errors than the GNU compiler. The point is that each compiler has things it is
fussy about. Know your compiler.
//File: test.const.cc
//to test attempt use of non-const arg for nonconst
//reference parameter. and to apply a nonconst member
//to a const object
#include <iostream>
using namespace std;

class C
{
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

43
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
public:
C();
C(int x);
// other members
void output(); //need void output() const; to avoid error
below
private:
int i;
};
C::C(){} //does nothing
C::C(int x) //could write C::C(int x):i(x){}
{
i = x;
}
const C x(1);
const int xx = 3;

void t(int& i){}

int main()
{
x.output(); //error: nonconst member called on a const
//object
t(xx); //error: const argument used for reference
//parameter
return 0;
}

/*
bcc32 -c test.const.cpp
Borland C++ 5.4 for Win32
Warning 25: Parameter 'i' is never used in function t(int &)
Warning 29: Non-const function C::output() called for const
object in function main()
Warning 30: Temporary used for parameter 'i' in call to
't(int &)' in function main()
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

44
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Compilation finished at Sun Dec 31 16:38:51

g++ -c test.const.cpp
In function 'int main()':
29: passing 'const C' as 'this' argument of 'void
C::output()' discards qualifiers
30: conversion from 'const int' to 'int &' discards
qualifiers
25: in passing argument 1 of 't(int &)'

Compilation exited abnormally with code 1 at Sun Dec 31
16:37:54
Overloading Operators
Suppose the compiler sees x and y in the context
x op y
where one of x or y is an object of class Some_Class . For the sake of definiteness,
suppose it is x that is of class Some_Class. Further, suppose 'op' is one of the
many C++ operators capable of being overloaded. The compiler then looks for a
function:
return_type operator op(Some_Class x, type_name y);
or for a member function
return_type Some_Class::operator op(type_name y);
to which the compiler generates a call.
For example, consider the infix expression
x + y
where for the variables x and y have been defined to have the int type. If the
operator op were overloaded as a standalone function, the infix expression is translated
into the direct call to the operator function,
operator+ (x, y);
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

45
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
If the operator op were overloaded as a standalone function, the infix expression is
translated into this direct call to the operator function,
x.operator+(y);
The programmer can actually write direct calls to the operator functions, but there is no
good reason to do so other than to verify that this is possible. Nevertheless, my students
want to call the operator functions directly instead of the infix operators. I found it
necessary to spend some time emphasizing that the compiler does that for them.
Some Rules
1. Precedence and arity (the number of arguments) of the operator cannot be changed by
overloading. For example, a binary / cannot be made to be a unary operator.
2. Operator functions for =, ->, [] and () must be non-static members of the
class to which they apply. (Overloading of the operators =, [] and -> is discussed
later in the text.)
The student must know and understand the rules listed on page 471 of the text.
Friends Vs Member overloading (for the instructor)
Please note that the text does not discuss overloading operators as members until Chapter
11.
There is a compelling argument in favor of overloading operators using friends rather
than overloading as members. Professor Savitch pointed out to me that he did not put
both in the text at this point because presumably the students only know about the friend
overloading method. It is worth pointing out to the instructor that if both operands are
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 or an amount of money in an
expression such as
object + 2
or
2 + object
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

46
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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.
More detail on operator overloading (for the instructor)
The decision on whether to write an overloading operator function as a stand-alone
function (most likely a friend of the class) or to write the operator function as a member
depends on several things. Is the operator expected to behave symmetrically between the
arguments? If so, the operator should be a standalone function (and probably a friend of
the class). If the asymmetry is desired or is no problem, then a member operator function
may be used.
Here is an example, stripped of all details except the minimum to illustrate the language
behavior. This code illustrates the essential asymmetry of operators overloaded as
members.
//File: overload1.cc
//to test behavior of member operator overloading.
//What conversions and when the conversions are made.

#include <iostream>
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

47
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
using namespace std;

class F
{
public:
F(){};
void operator +(Fx);
F(int);
operator int ();
};

F::F(){} //does nothing

F::F(int)
{
cout << "promoting int to F" << endl;
}

void F::operator +(F x)
{
cout << "operator+ called " << endl;
}

F::operator int (); //
3

{
cout << "demoting F to int " << endl;
return 1;
}

int main()
{

3
The usage operator int(...) is not discussed in the text. The operator function
member used here is for conversion from a class F object to an int value. The
keywords for all built-in types can be overloaded to convert a class object to the built-in
type in a way appropriate for the requirements of our class.

Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

48
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
F x;
int i = 2;
cout << "F x; int i; x+i; Here i is promoted, and "
<< "operator+(F)is called." << endl;
x + i; // only calls operator +, returned value ignored
cout << "cout << i+x << endl; x is demoted, and "
<< "the + adds two ints here." << endl;
cout << i + x << endl;
return 0;
}
The output of this is:
F x; int i; x+i; Here i is promoted, and operator+(F)is
called.
promoting int to F
operator+ called
cout << i+x << endl; x is demoted, and the + adds two ints
here.
demoting F to int
3
Note the asymmetric behavior between
int_object + F_object
and
F_object + int_object.
If this had been overloaded as a friend function, the behavior would have been
symmetric. The following example illustrates the idea. It requires a few more details to
illustrate the point than the earlier exercise.
//File: overload2.cc
//to investigate exactly what conversions are made in a
//call to operator overloadings, and when these conversions
//are made.

#include <iostream>
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

49
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
using namespace std;

class F
{
public:
friend F operator +(F u, F v);
F(int q);
operator int ();
private:
int p;
};

F::F(int q)
{
p = q;
cout << "promoting int to F" << endl;
}
F::operator int () // See the earlier note on this
overloading.
{
cout << "demoting F to int " << endl;
return p;
}

F operator +(F u, F v)
{
cout << "operator+(F,F) called " << endl;
F x(u.p + v.p);
cout << "still inside the operator+ function " << endl;
return x;
}

int main()
{
cout << "starting main, F x = 1;" << endl;
F x = 1;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

50
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
int i = 2;
cout << "F x; int i; x+i; Here i is promoted, and "
<< "operator+(F, F)is called." << endl;
cout << x + i << endl; // converts from F to int

cout << "after first section" << endl << endl;
cout << "cout << i+x << endl; i is promoted, and "
<< "the operator+(F, F) is called here." << endl;
cout << i + x << endl; // converts from F to int.
return 0;
}
Output from this code is:
starting main, F x = 1;
promoting int to F
F x; int i; x+i; Here i is promoted, and operator+(F, F)is
called.
promoting int to F
operator+(F,F) called
promoting int to F
still inside the operator+ function
demoting F to int
3
after first section

cout << i+x << endl; i is promoted, and the operator+(F, F)
is called here.
promoting int to F
operator+(F,F) called
promoting int to F
still inside the operator+ function
demoting F to int
3
Note that in each case the arguments are promoted to F objects.
Overloading >> and << (the extractors and inserters)
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

51
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
If we are the class author, we can add a member overloading
operator <<(ourclass object)
to our class definition. However, 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 io in the streams library. Our I/O overloading would
not be in anyone else's I/O library. How then do we obtain stream I/O for a user defined
class?
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;
Operator Overloading using void return type
While the text always overloads the << and >> operators to return a stream reference,
some instructors, in fact, some programmers, 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 behaves 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:
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;
Savitch Instructors Resource Guide
Problem Solving w/ C++, 6e Chapter 11

52
Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
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 11.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 11.8 in the text.
//no return statement
}
Similar arrangements work for overloading operator <<.

Você também pode gostar