Section 5 - Exceptions - Full Listing

5B.5.1 Rational Number Class Listing

This is a listing of the entire program and Rational number class in case you want to try it and/or modify it. 

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

// Rational prototype --------------------------------
class Rational
{
  // friend operators
  friend Rational operator+(Rational r1, Rational r2);
  friend Rational operator-(Rational r1, Rational r2);
  friend ostream & operator<<(ostream& strm, Rational& r);

private:
  long   num, den;
  long gcd();
  void reduce();

public:
  Rational( long n = 0, long d = 1 );
  Rational( string ratString );
  void set( long n = 0, long d = 1 );
  void set( string ratString );
  void show();

  // exception classes
  class BadRationalString { };
  class ZeroDenominator { };
};

// main method ---------------------------------------
int main()
{
  try
  {
    Rational a(1, 2), b(3, 4), c("+5/4"), d("50/-90"), e(5), f("1000");

    cout << "a: "  << a << " b: " << b << endl;
    cout << "c: "  << c << " d: " << d << endl;
    cout << "e: "  << e << " f: " << f << endl << endl;
  }
  catch (Rational::BadRationalString)
  {
    cout << "*** bad string ***"  << endl;
  }
  catch (Rational::ZeroDenominator)
  {
    cout << "*** zero denominator ***"  << endl;
  }

  Rational a, b, c, d;
  try
  {
    a.set(3244, 22);
    cout << "a: " << a << endl;
    b.set("45/5");
    cout << "b: " << b << endl;
    c.set("12.3/8");
    cout << "c: " << c << endl;
    d.set(8443, 0);
    cout << "d: " << d << endl;
  }
  catch (Rational::BadRationalString)
  {
    cout << "*** bad string ***"  << endl;
  }
  catch (Rational::ZeroDenominator)
  {
    cout << "*** zero denominator ***"  << endl;
  }
  return 0;
}

// Rational method definitions --------------------------
Rational::Rational(long n, long d)
{
  set(n, d);
}

Rational::Rational(string ratString)
{
  set(ratString);
}

void Rational::set(string ratString)
{
  long tryNum, tryDen, slashPos, lengthOfDenStr;
  string strNum, strDen;
  int k;

  // contingency values in case we throw exception
  num = 0;
  den = 1;

  // must be of form "int/int"
  slashPos = ratString.find("/");
  if (slashPos == 0 || slashPos > 50)
  throw BadRationalString();  // returns immediately

  if ( slashPos < 0 )
  {
    // no "/". hopefully just an int
    strNum = ratString;
    strDen = "1";
  }
  else
  {
    strNum = ratString.substr(0, slashPos);
    lengthOfDenStr = ratString.length() - slashPos - 1;
    strDen = ratString.substr(slashPos+1, lengthOfDenStr);
  }

  // make sure strNum is valid number
  // first digit can be digit, or +/-
  if ( !isdigit(strNum[0] ) && strNum[0] != '-' && strNum[0] != '+')
    throw BadRationalString(); // returns immed.

  // rest must be digits
  for (k = 1; k < strNum.length(); k++)
    if ( !isdigit(strNum[k]) )
      throw BadRationalString(); // returns immed.

  // same test for strDen
  if ( !isdigit(strDen[0] ) && strDen[0] != '-' && strDen[0] != '+')
    throw BadRationalString(); // returns immed.

  for (k = 1; k < strDen.length(); k++)
    if ( !isdigit(strDen[k]) )
      throw BadRationalString(); // returns immed.

  // convert to ints
  istringstream(strDen) >> tryDen;
  istringstream(strNum) >> tryNum;
  set(tryNum, tryDen);
}

void Rational::set(long n, long d)
{
  // defaults in case of user error (bad d)
  num = 1 ; den = 0;
  if (d==0)
  throw ZeroDenominator();
  num = n;
  den = d;
  reduce();
}


long Rational::gcd() // greatest common denominator of num and den
{
  long temp, n, d;
  for ( n = num,  d = den; d != 0;  )
  {
  temp = n % d;
  n = d;
  d = temp;
  }
  return n;
}

void Rational::reduce()
{
  long myGcd = gcd();
  if (myGcd > 1)
  {
    num /= myGcd;
    den /= myGcd;
  }
  if (den < 0)
  {
    den = -den;
    num = -num;
  }
}

void Rational::show()
{
  cout << " " << num;
  if (den != 1)
    cout << "/" << den;
}

// end Rational method definitions ---------------------

// overloaded operator functions   ---------------------
Rational operator+(  Rational r1, Rational r2)
{
  Rational temp;

  temp.num = r1.num * r2.den + r1.den * r2.num;
  temp.den = r1.den * r2.den;
  temp.reduce();
  return temp;
}

Rational operator-(Rational r1, Rational r2)
{
  Rational temp;

  temp.num = r1.num * r2.den - r1.den * r2.num;
  temp.den = r1.den * r2.den;
  temp.reduce();
  return temp;
}
ostream & operator<<(ostream& strm, Rational& r)
{
    strm << " " << r.num;
    if (r.den != 1)
      strm << "/" << r.den;
    return strm;
}