Handout 21

The Rational Class


#include <iostream.h>
class Rational {
  friend ostream & operator << (ostream & , Rational ); // Note 1
  friend Rational operator+(long, Rational); // long + rational
public:
  Rational(long = 0, long = 1); // Constructor with parms
  Rational(const Rational &); // Copy Constructor - Note 2
  Rational operator=(Rational); // assignment operation
  Rational operator+(Rational); // rational + rational
  Rational operator+(long); // rational + long
  bool operator==(Rational);
private:
  long num;
  long denom;
};

// constructor with parameters
Rational::Rational(long a, long b) {
  num = a;
  denom = b;
  if(denom < 0) { num *= -1; denom *= -1;} // keep minus in numerator
}
// copy constructor
Rational::Rational(const Rational &A) {
  num = A.num;
  denom = A.denom;
  cout << "copy constructor called" << endl;
}
// rational = rational
Rational Rational::operator=(Rational B) {
  num = B.num;
  denom = B.denom;
  return *this;
}
// rational + rational
Rational Rational::operator+(Rational B) {
  Rational temp(0,1);
  temp.num = B.denom * num + denom * B.num;
  temp.denom = denom * B.denom;
  if(temp.denom < 0) { temp.num *= -1; temp.denom *= -1; } // keep minus in numerator
  return temp;
}
// rational + long
Rational Rational::operator+(long B) {
  Rational temp(0,1);
  temp.num = denom * B + num;
  temp.denom = denom;
  if(temp.denom < 0) { temp.num *= -1; temp.denom *= -1; } // keep minus in numerator
  return temp;
}
// rational == rational
bool Rational::operator==(Rational B) {
  if(num == B.num && denom == B.denom) return true;
  else return false;
}

// *************** the friend functions *****************************
// long + rational
Rational operator+(long B, Rational A) {
  Rational temp(0,1);
  temp.num = A.denom * B + A.num;
  temp.denom = A.denom;
  if(temp.denom < 0) { temp.num *= -1; temp.denom *= -1; } // keep minus in numerator
  return temp;
}

ostream & operator << (ostream & os, Rational A) {
  os << A.num << "/" << A.denom;
  return os; // enables cascading
}
// *************** main *****************************
int main() {
  Rational X(0,0), Y(1,2), Z(3,4), Q;
  int i;
  
  cout << Q << endl;

  X = Y;
  cout << X << endl;
  X = Z + (long)1;
  cout << X << endl;
  X = (long)1 + Z;
  cout << X << endl;
  if(Y == Z) cout << "Y == Z" << endl;
  else cout << "Y != Z" << endl;
  cin >> i;
  
  return 0;
}

Program Output

copy constructor called
0/1
copy constructor called
copy constructor called
copy constructor called
1/2
copy constructor called
copy constructor called
copy constructor called
copy constructor called
7/4
copy constructor called
copy constructor called
copy constructor called
copy constructor called
copy constructor called
7/4
copy constructor called
Y != Z

The Notes:
source code
0 - note that this example does not include the reduction to simpliest form.
1 - the << operator must be a friend function since is accesses private data elements.
2 - a copy constructor is called whenever an object is needed as in a call-by-value argument, returning a object by value or initializing an object to be a copy of another object, as in:

      Rational X(1,2);

      Rational Y = X;

It's always a good practice to include a copy constructor since it will eliminate obscure errors when passing arguments by reference, etc.