In this post I will demonstrate how you can implement the representation of the 4 number formats: whole, Rational, Real and Complex numbers..

However different those types are, we still can distinguish between common things in between: we can add any 2 numbers of any 2 different types, as well as subtract and multiply, along with other complex operations, but for the sake of simplicity, I will stick only with the above.

That calls up the notion of Inheritance! it means, we will be creating a Base class, called Number, and another 4 calsses per number type, that will inherent the fundamental characteristics from class Number – the Base class.

Shared Pointer:
Since working with different types, we need somehow to cast them in order to keep up with the math convention,  it means that when adding a Whole number with complex, the result will be a complex number. The following chart clarifies the castings:

chart
Those relations of casting are valid to adding, subtraction and multiplying

I made use of the shared pointer that points at the Base object, Number, in order to get rid of the pain of tackling with casting in the mid of the program.

Class Number:

#ifndef NUMBER_H
#define NUMBER_H

#include <iostream>
#include <memory>

namespace mtm {

  enum Type { REAL, COMPLEX, RATIONAL, WHOLE };

  typedef std::shared_ptr<class Number> number_ptr;

  class Number
  {
    const Type m_Type;
  public:
    Number(const Type& t) : m_Type(t) {}
    virtual ~Number() {}

    /** Return the type of number */
    const Type& get_type() const { return m_Type; }

    /** Return an instance of a number with the same type as this one, but with a value of 0 */
    virtual number_ptr zero() const = 0;

    /** Add the parameter to the current number and return the sum */
    virtual number_ptr add(const Number&) const = 0;

    /** Subtract the parameter from the current number and return the difference */
    virtual number_ptr subtract(const Number&) const = 0;

    /** Multiply the number by the parameter and return product */
    virtual number_ptr multiply(const Number&) const = 0;

    /** Return the real part of the number as a floating point value */
    virtual const double get_real_value() const = 0;

    /** Print the number's value in human readable form */
    virtual void print(std::ostream& os) const = 0;
  };

} // namespace mtm

#endif // NUMBER_H

Note that this class is abstract, as it has all the methods as pure virtual!
Next, we will implemnt the other 4 classes. each class will be made of a header file(*.h) and source(*.cpp) per type.

Whole:
Header:

#ifndef WHOLE_H_
#define WHOLE_H_

#include "complex.h"
#include "number.h"
#include "real.h"
#include "rational.h"
#include "operators.h"
namespace mtm {
/*
 * Inherits from Number and represents integer.
 */
	class Whole: public Number {
	private:
		int whole;
	/**************************************************************************/
	public:
	/***********************Constructors***************************************/
		Whole(int num = 0) :
				Number(WHOLE), whole(num) {
		}
		virtual number_ptr add(const Number&) const;
		virtual number_ptr zero() const;
		virtual number_ptr subtract(const Number&) const;
		virtual number_ptr multiply(const Number&) const;
		virtual const double get_real_value() const ;
		virtual void print(std::ostream& os) const;
	};
	/****************************Operator declaration**************************/
	number_ptr operator+(const Whole& num1, const Number& num);
	number_ptr operator-(const Whole& num1, const Number& num);
	number_ptr operator*(const Whole& num1, const Number& num2);

}
#endif /* WHOLE_H_ */

 Whole.cpp:

#include "whole.h"
const int NEGATIVE = -1;
namespace mtm {
	number_ptr Whole::add(const Number& num) const  {
		return *this + num;
	}
	/**************************************************************************/
	number_ptr Whole::subtract(const Number& num) const {
		return *this - num;
	}
	/**************************************************************************/
	 number_ptr Whole::multiply(const Number& num) const {
		 return *this * num;
	 }
	/**************************************************************************/
	 number_ptr Whole::zero() const {
		number_ptr ptr = number_ptr(new Whole(0));
		return ptr;
	}
	 /*************************************************************************/
	 const double Whole::get_real_value() const {
		return (double)(this->whole);
	}
	 /*************************************************************************/
	 void Whole::print(std::ostream& os) const {
	 			os << this->whole;
	 		}
	/**************************************************************************/
    /*********************OPERATOROVERLOADING_IMPLEMENTATION*******************/
	number_ptr operator+(const Whole& num1, const Number& num) {
		if(num.get_type() == WHOLE) {
			return number_ptr
					(new Whole(num1.get_real_value() + num.get_real_value()));
		}
		return num.add(num1);
	}
	/**************************************************************************/
	number_ptr operator-(const Whole& num1, const Number& num){
		if(num.get_type() == WHOLE) {
			return number_ptr
					(new Whole(num1.get_real_value() - num.get_real_value()));
		}
		Whole inverted = Whole((NEGATIVE) * num1.get_real_value());
		Whole inverter = Whole(NEGATIVE);
		return (num.add(inverted))->multiply(inverter);
	}
	/**************************************************************************/
	number_ptr operator*(const Whole& num1, const Number& num2) {
		if(num2.get_type() == WHOLE) {
			return number_ptr
					(new Whole(num1.get_real_value() * num2.get_real_value()));
		}
		return num2.multiply(num1);
	}
	/**************************************************************************/
}

 Real.h:

#ifndef REAL_H_
#define REAL_H_

#include "number.h"
#include "operators.h"
namespace mtm {
/*
 * Inherits from Number
 * holds a value of double as the real value of a real numbert
 */
	class Real: public Number {
	private:
		double real;
	public:
		Real(double real = 0) : Number(REAL), real(real) {}
		/******************INHERTED_VIRTUAL_METHODS****************************/
		virtual number_ptr zero() const;
		virtual number_ptr add(const Number&) const;
		virtual number_ptr subtract(const Number&) const;
		virtual number_ptr multiply(const Number&) const;
		virtual const double get_real_value() const;
		virtual void print(std::ostream& os) const;
		/********************_END_OF_CLASS_MTHOD_DECLARATION*******************/
	};
	/******************_OPERATOE_OVERLOADING_**********************************/
	number_ptr operator+(const Real& num1, const Number& num);
	number_ptr operator-(const Real& num1, const Number& num);
	number_ptr operator*(const Real& num1, const Number& num2);

}
#endif /* REAL_H_ */

 Real.cpp

#include "complex.h"
#include "real.h"
#include "rational.h"
#include "whole.h"

namespace mtm {
	/*********************_VIRTUAL_METHODS_IMPLEMENTATION**********************/
	number_ptr Real::add(const Number& num) const  {
		return *this + num;
	}
	/**************************************************************************/
	number_ptr Real::subtract(const Number& num) const {
		return *this - num;
	}
	/**************************************************************************/
	number_ptr Real::multiply(const Number& num) const {
		return *this * num;
	}
	/**************************************************************************/
	 void Real::print(std::ostream& os) const {
		os << this->real;
	}
	/**************************************************************************/
	 const double Real::get_real_value() const {
		return (double)(this->real);
	}
	/**************************************************************************/
	 number_ptr Real::zero() const {
		return number_ptr(new Real(0));
	}
	/**************************************************************************/
	/****************_OPERATOR_OVERLOADING_IMPLEMENTATION_*********************/
	/**************************************************************************/
	number_ptr operator+(const Real& num1, const Number& num) {
		if(num.get_type() != COMPLEX) {
			if(num.get_type() == RATIONAL) {
				const Rational& temp = dynamic_cast<const Rational&>(num);
				double realValue =
						(num1.get_real_value() * temp.getDenominator() +
						temp.getNumerator()) / temp.getDenominator();
				return number_ptr(new Real(realValue));
			}
			return number_ptr
					(new Real(num1.get_real_value() + num.get_real_value()));
		}
		return num.add(num1);
	}
	/**************************************************************************/
	number_ptr operator*(const Real& num1, const Number& num2) {
		if(num2.get_type() != COMPLEX) {
			return number_ptr
					(new Real(num1.get_real_value() * num2.get_real_value()));
		}
		return num2.multiply(num1);
	}
	/**************************************************************************/
	number_ptr operator-(const Real& num1, const Number& num) {
		if(num.get_type() != COMPLEX) {
			return number_ptr
					(new Real(num1.get_real_value() - num.get_real_value()));
		}
		Real inverted = Real((-1) * num1.get_real_value());
		Real inverter = Real(-1);
		return (num.add(inverted))->multiply(inverter);
	}
}

 Rational.h

/*
 * Rational.h
 *
 *  Created on: Jan 5, 2015
 *      Author: bassam
 */

#ifndef RATIONAL_H_
#define RATIONAL_H_

#include "number.h"
#include "complex.h"
#include "defs.h"
#include "operators.h"

namespace mtm {
/*
 * Inherits from Number
 * represent rationl numbers with numenerator and denomerator.
 */
	class Rational: public Number {
	private:
		int numerator;
		int denominator;
	public:
		Rational(int numerator = 0, int denominator = 1);
		/**********************************************************************/
		virtual number_ptr zero() const;
		virtual number_ptr add(const Number&) const;
		virtual number_ptr subtract(const Number&) const;
		virtual number_ptr multiply(const Number&) const;
		virtual const double get_real_value() const;
		virtual void print(std::ostream& os) const ;
		/************************Helping Methods*******************************/
		int getNumerator() const;
		int getDenominator() const;
		/*******************_END_OF_RATIONAL_CLASS_MRTHODS_********************/
	};
	/********************_OPERATORS_*******************************************/
	number_ptr operator*(const Rational& num1, const Number& num2);
	number_ptr operator+(const Rational& num1, const Number& num2);
	number_ptr operator-(const Rational& num1, const Number& num2);
}
#endif /* RATIONAL_H_ */

 Rational.cpp

/*
 * Rational.cpp
 *
 *  Created on: Jan 5, 2015
 *      Author: bassam
 */

#include "complex.h"
#include "real.h"
#include "rational.h"
#include "whole.h"

const int ZERO = 0;
namespace mtm {
	Rational::Rational(int up, int down) :
			Number(RATIONAL), numerator(up), denominator(down) {
		if(denominator == 0) {
			throw DivisionByZero();
		} else if(denominator < 0) {
			numerator = numerator * (-1);
			denominator = denominator * (-1);
		}
	}
	/**************************************************************************/
	void Rational::print(std::ostream& os) const {
		if(this->numerator == -0) {
			os << ZERO << "/" << this->denominator;
		} else if((this->numerator < 0) ||(this->denominator < 0)) {
			os << -abs(this->numerator) << "/" << abs(this->denominator);
		} else {
			os <<  this->numerator << "/" << this->denominator;
		}
	}
	/**************************************************************************/
	const double Rational::get_real_value() const {
		return ((double)(this->numerator) / (double)(this->denominator));
	}
	/**************************************************************************/
	number_ptr Rational::zero() const {
		return number_ptr(new Rational(0,1));
	}
	/**************************************************************************/
	number_ptr Rational::add(const Number& num) const  {
		return *this + num;
	}
	/**************************************************************************/
	number_ptr Rational::subtract(const Number& num) const {
		return *this - num;
	}
	/**************************************************************************/
	number_ptr Rational::multiply(const Number& num) const {
		return *this * num;
	}
	/**************************************************************************/
	/*
	 * returns the value of the private field 'numerator' -
	 *  without giving access to change it
	 */
	int Rational::getNumerator() const {
		return this->numerator;
	}
	/**************************************************************************/
	/*
	 * returns the value of the private field 'denominator' -
	 *  without giving access to change it
	 */
	int Rational::getDenominator() const {
		return this->denominator;
	}
	/**************************************************************************/
	/*
	 * overloading of operator+ between a rational and a Number
	 */
	number_ptr operator+(const Rational& num1, const Number& num2) {
		if(num2.get_type() == RATIONAL) {
			const Rational& temp = dynamic_cast<const Rational&>(num2);
			if(num1.getDenominator() == temp.getDenominator()) {
				return number_ptr(new Rational(num1.getNumerator() +
						temp.getNumerator(), num1.getDenominator()));
			}
			const int down = num1.getDenominator() * temp.getDenominator();
			const int up = num1.getNumerator() * temp.getDenominator()
					+ num1.getDenominator() * temp.getNumerator();
			return number_ptr( new Rational(up,down));
		} else if(num2.get_type() == WHOLE) {
			const int up = num1.getNumerator() + num1.getDenominator() *
					num2.get_real_value();
			Rational sum = Rational(up ,num1.getDenominator());
			return number_ptr( new Rational(sum));
		}
		return num2.add(num1);
	}
	/**************************************************************************/
	/*
	 * overloading of operator* between a rational and a Number
	 */
	number_ptr operator*(const Rational& num1, const Number& num2) {
		if(num2.get_type() == WHOLE) {
			return number_ptr(new Rational(num1.getNumerator() *
					(int)num2.get_real_value(), num1.getDenominator()));
		} else if(num2.get_type() == RATIONAL) {
			const Rational& temp = dynamic_cast<const Rational&>(num2);
			return number_ptr(new Rational(num1.getNumerator() *
			temp.getNumerator(), num1.getDenominator() *
					temp.getDenominator()));
		}
		return num2.multiply(num1);
	}
	/**************************************************************************/
	/*
	 * overloading of operator- between a rational and a Number
	 */
	number_ptr operator-(const Rational& num1, const Number& num2) {
		if(num2.get_type() == RATIONAL) {
			const Rational& temp = dynamic_cast<const Rational&>(num2);
			if(temp.getDenominator() == num1.getDenominator()) {
				return number_ptr(new Rational(num1.getNumerator() -
						temp.getNumerator(), num1.getDenominator()));
			}
			int up = num1.getNumerator() * temp.getDenominator() -
					num1.getDenominator() * temp.getNumerator();
			int down = num1.getDenominator() * temp.getDenominator();
			return number_ptr(new Rational(up, down));
		} else if(num2.get_type() == WHOLE) {
			return number_ptr(new Rational(num1.getNumerator() -
						num1.getDenominator() * num2.get_real_value(),
						num1.getDenominator()));
		}
		Rational inverted = Rational((-1) * num1.getNumerator(),
				num1.getDenominator());
		Rational inverter = Rational(-1,1);
		return (num2.add(inverted))->multiply(inverter);
	}
}

 Complex.h

#ifndef COMPLEX_H_
#define COMPLEX_H_

#include "number.h"
#include "operators.h"
namespace mtm {
/*
 * Inherts from Number basic virtual functions.
 */
	class Complex: public Number {
	private:
		double real;
		double imaginary;
	public:
		Complex(double real = 0, double imaginary = 0) :
				Number(COMPLEX), real(real), imaginary(imaginary) {
		}
		/**********************************************************************/
		virtual const double get_real_value() const;
		virtual number_ptr add(const Number& number) const;
		virtual number_ptr zero() const;
		virtual number_ptr subtract(const Number&) const;
		virtual number_ptr multiply(const Number&) const;
		virtual void print(std::ostream& os) const;
		/************************Getters***************************************/
		double getImaginaryValue() const;
	};
	/************************Operators*****************************************/
	number_ptr operator*(const Complex& num1, const Number& num2);
	number_ptr operator+(const Complex& num1, const Number& num2);
	number_ptr operator-(const Complex& num1, const Number& num2);
}

#endif /* COMPLEX_H_ */

 Complex.cpp

#include "complex.h"
#include "real.h"
#include "rational.h"
#include "whole.h"
namespace mtm {

/*
 * Handels the representation of a complex number.
 */
		const double Complex::get_real_value() const {
			return (this->real);
		}
		/**********************************************************************/
		number_ptr Complex::add(const Number& num) const {
			return *this + num;
		}
		/**********************************************************************/
		number_ptr Complex::multiply(const Number& num) const {
			return *this * num;
		}
		/**********************************************************************/
		number_ptr Complex::subtract(const Number& num) const {
			return *this - num;
		}
		/**********************************************************************/
		number_ptr Complex::zero() const {
			return number_ptr(new Complex());
		}
		/**********************************************************************/
		void Complex::print(std::ostream& os) const {
			if (imaginary < 0) {
				os << this->real << this->imaginary << "i";
			} else {
				os <<  this->real << "+" << this->imaginary << "i";
			}
		}
		/**********************************************************************/
		double Complex::getImaginaryValue() const {
			return this->imaginary;
		}
		/**********************************************************************/
		/**********************_OPERATOR_OVERLOADING_**************************/
		number_ptr operator+(const Complex& num1, const Number& num2) {
			if(num2.get_type() == COMPLEX) {
				const Complex& temp = dynamic_cast<const Complex&>(num2);
				return number_ptr(new Complex(num1.get_real_value() +
						temp.get_real_value(), num1.getImaginaryValue() +
						temp.getImaginaryValue()));
			}
			double real = num1.get_real_value() + num2.get_real_value();
			return number_ptr(new Complex(real, num1.getImaginaryValue()));
		}
		/**********************************************************************/
		number_ptr operator-(const Complex& num1, const Number& num2) {
			if(num2.get_type() == COMPLEX) {
				const Complex& temp = dynamic_cast<const Complex&>(num2);
				return number_ptr(new Complex(num1.get_real_value() -
						temp.get_real_value(), num1.getImaginaryValue() -
						temp.getImaginaryValue()));
			}
			return number_ptr(new Complex(num1.get_real_value() -
					num2.get_real_value(), num1.getImaginaryValue()));
		}
		/**********************************************************************/
		number_ptr operator*(const Complex& num1, const Number& num2) {
			if(num2.get_type() == COMPLEX) {
				const Complex& temp = dynamic_cast<const Complex&>(num2);
				double real = (num1.get_real_value() * num2.get_real_value());
				real -= num1.getImaginaryValue() * temp.getImaginaryValue();
				double imaginary = num1.get_real_value() *
						temp.getImaginaryValue();
				imaginary += num1.getImaginaryValue() * temp.get_real_value();
				return number_ptr(new Complex(real, imaginary));
			}
			return number_ptr(new Complex(num1.get_real_value() *
					num2.get_real_value(),num2.get_real_value() *
					num1.getImaginaryValue()));
		}
}

In order to handle all the arithmetic operations, i added another header file with all the declarations of the operators in it:
Operators.h

/*
 * operators.h
 *
 *  Created on: Jan 16, 2015
 *      Author: bassam
 */

#ifndef OPERATORS_H_
#define OPERATORS_H_

#include "number.h"
/*
 * Operates on shared pointers with Number as the pointed object.
 */

namespace mtm {
/*
 * Subtractes 2 shared pointers.
 * input: 2 shared poiters.
 * output: none.
 * return: shared poiter to the result.
 */
	inline number_ptr operator-(const number_ptr& num1,
			const number_ptr&  num2) {
		if(num2 == NULL) {
			return num1;
		} else if(num1 == NULL) {
			return num2->subtract(*(num2->add(*num2)));
		}
		return((*num1).subtract(*num2));
	}
/******************************************************************************/
/*
 * Addition of 2 shared pointers.
 * input: 2 shared poiters.
 * output: none.
 * return: shared poiter to the result.
 */
	inline number_ptr operator+(const number_ptr num1, const number_ptr  num2) {
		if(num1 == NULL && num2 == NULL) {
			return number_ptr();
		} else if(num1 == NULL) {
			return num2;
		} else if (num2 == NULL) {
			return num1;
		}
		return((*num1).add(*num2));
	}
/******************************************************************************/
/*
 * Product of 2 shared pointers.
 * input: 2 shared poiters.
 * output: none.
 * return: shared poiter to the result.
 */
	inline number_ptr operator*(const number_ptr num1, const number_ptr  num2) {
		if(num1 == NULL || num2 == NULL) {
			return number_ptr();
		}
		return((*num1).multiply(*num2));
	}
/******************************************************************************/
}

#endif /* OPERATORS_H_ */

the compilation of the above files is done via the command(if you happen too be working in linux/UNIX):

g++ -std=c++11 -Wall -pedantic-errors -Werror -DNDEBUG whole.cpp real.cpp rational.cpp complex.cpp -o myOut
#then, excute the binary obj:
./myOut
Advertisements