Overloading Operators: Creating a Rational Class

Overloading Operators Creating a Rational Class
This tutorial will teach you how to overload most of the operators available in C++, including arithmetic and comparison, in order to create a class that is able to calculate rational numbers.

Probably one of the most interesting features of object oriented programming is the possibility to overload operators. This allows you to create your own class that handles the +, –, * and all the other arithmetical operators. It also allows you to override comparison operators such as ==, < and <= as well. By being able to do all this, you can create your own data types, just like int and float is. One of these data types is the rational type, which we will be creating today. The class we’re creating is slightly incomplete since it’s missing a few operators, however it does contain the most important ones and from that point on it’s easy for someone to complete it with the remaning operators.

What if you would have to create a class that is able to add rational numbers such as 1/2 + 3/4? The qiuck and dirty way would be to create functions and then use them like this:

string strRational = RationalAdd(1, 2, 3, 4); // Add 1/2 to 3/4 and store the result in a string
Surely this can’t be the best you can do. So let’s overload some operators.

We begin with the includes and the std namespace:

#include

#include

using namespace std;
Every data type is really a class, so what we need to create first is a class:

class Rational

{

    private:

        int num;

        int den;

    public:

        // Accessor and mutator functions

        void setNumerator(int);

        void setDenominator(int);

        int getNumerator();

        int getDenominator();

        // Greatest common divisor, needed to simplify fractions

        int gcd();

        // Simplifies fractions

        void simplify();

        // Arithmetic operators

        Rational operator+(Rational);

        Rational operator-(Rational);

        Rational operator*(Rational);

        Rational operator/(Rational);

        // Comparison operators

        bool operator==(Rational);

        bool operator!=(Rational);

        bool operator>(Rational);

        bool operator<(Rational);

        bool operator>=(Rational);

        bool operator<=(Rational);

        // Unary operator

        Rational operator-();

};
This class has a whole lot of public functions and only two private members: num and den. Each instance of the Rational class represents a rational number, and num and den represent the numerator and denominator of that rational number. Now the first four functions are pretty easy to figure out, they set and retrieve the numerator and denominator of the rational number. Let’s see their definition here:

void Rational::setNumerator(int n)

{

    num = n;

}

 

void Rational::setDenominator(int n)

{

    den = n;

}

 

int Rational::getNumerator()

{

    return num;

}

 

int Rational::getDenominator()

{

    return den;

}
The gcd() function finds the greatest common divisor and it will be the engine behind the simplify() function which will follow next:

// Greatest common divisor

int Rational::gcd()

{

    int a = num;

    int b = den;

    int tmp;
    // While b is not 0

    while (b)

    {

        tmp = b;

        b = a % b;

        a = tmp;

    }

    return a;

}
The simplify() function is extremely important, as it will allow us to simplify the rational number to its simplest form. If the number is 4/8 it will simplify it to 1/2. Otherwise, if we’d try to later compare 4/8 with 1/2 and see if they are equal, we’d get a false response, even though they are really equal. This way, by being able to simplify both numbers, we are making sure that if they are equal they will both have the same numerator and denominator. simplify() merely makes a call to the gcd() function and then divides both numerator and denominator by the number return from gcd().

// Simplifies the fraction

void Rational::simplify()

{

    // Get the greatest common divisor

    int gcdNum = gcd();

    // If there is a common divisor, we don’t want to divide by 0

    if(gcdNum != 0)

    {

        // Set the new numerator

        num = num / gcdNum;

        // Set the new denominator

        den = den / gcdNum;

    }

}
And now comes the most interesting part, implementing the arithmetic operators. They all take an object of type Rational (i.e. a rational number) and return an object of type Rational (i.e. a rational number.)

Rational Rational::operator+(Rational ratPassed)

{

    // Adding fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den + den * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator-(Rational ratPassed)

{

    // Subtracting fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den – den * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator*(Rational ratPassed)

{

    // Multiplying fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator/(Rational ratPassed)

{

    // Dividing fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den;

    ratResult.den = den * ratPassed.num;

    return ratResult;

}
What these functions really do is to calculate two fractions using basic mathematical rules on how to add, subtract, multiply and divide. They have quite an interesting signature though – Rational Rational::operator+(Rational ratPassed) – one where you specify the operator that you want to define (such as +, -, *, /) preceeded by “operator” mark.

Let’s look at the addition operator:

Rational Rational::operator+(Rational ratPassed)

{

    // Adding fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den + den * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}
Here’s an example that will make a call to that function:

Rational ratResult = rat1 + rat2;

In the function definition ratPassed is rat2, while the instantiated Rational class is rat1. Thus when we call ratPassed.num and ratPassed.den we refer to rat2, and when we call num and den we call the num and den of rat1.

Let’s see the functions of the comparison operators:

bool Rational::operator==(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (ratPassed.num == num) && (ratPassed.den == den);

}

 

bool Rational::operator!=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (ratPassed.num != num) && (ratPassed.den != den);

}

 

bool Rational::operator>(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) > (ratPassed.num * den);

}

 

bool Rational::operator<(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) < (ratPassed.num * den);

}

 

bool Rational::operator>=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) >= (ratPassed.num * den);

}

 

bool Rational::operator<=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) <= (ratPassed.num * den);

}
They’re simpler than the arithmetical functions because you just do comparisons in the function itself, and then return the result of that comparison. But before you do that, each fraction is simplified using the simplify() function.

There’s one type of function left that handles a unary operator:

Rational Rational::operator-()

{

    // Unary operator multiplies the denominator by -1

    Rational ratResult;

    ratResult.den = den * -1;

    return ratResult;

}
This operator is used as such:

Rational newRat = -oldRat;

All this will do is to convert a positive fraction to a negative fraction and a negative fraction to a positive fraction by multiplying its numerator and denominator by -1. But what’s the difference between this and the – (minus) operator that subtracts two fractions when it comes to the function prototype? Well, when subtracting two fractions you take the second fraction as a parameter, and that’s reflected in the function prototype. With the unary operator you only use one fraction, and you already have that as the instance of the object.

That’s pretty much it for this tutorial, from this point on hopefully it’s clear to you how to implement the remaining operators. If not, feel free to post a comment.
Following is the full source code for this application:

#include

#include

using namespace std;

 

class Rational

{

    private:

        int num;

        int den;

    public:

        // Accessor and mutator functions

        void setNumerator(int);

        void setDenominator(int);

        int getNumerator();

        int getDenominator();

        // Greatest common divisor, needed to simplify fractions

        int gcd();

        // Simplifies fractions

        void simplify();

        // Arithmetic operators

        Rational operator+(Rational);

        Rational operator-(Rational);

        Rational operator*(Rational);

        Rational operator/(Rational);

        // Comparison operators

        bool operator==(Rational);

        bool operator!=(Rational);

        bool operator>(Rational);

        bool operator<(Rational);

        bool operator>=(Rational);

        bool operator<=(Rational);

        // Unary operator

        Rational operator-();

};

 

int main()

{

    // Preparing the numerator of the first rational number

    int ratNum1 = 0;

    cout << “Enter the denominator of the first rational number: “;

    cin >> ratNum1;

 

    // Preparing the denominator of the first rational number

    int ratDen1 = 0;

    cout << “Enter the numerator of the first rational number: “;

    cin >> ratDen1;

 

    // Create our first rational number

    Rational rat1;

    rat1.setNumerator(ratNum1);

    rat1.setDenominator(ratDen1);

 

    // Preparing the numerator of the second rational number

    int ratNum2 = 0;

    cout << “Enter the denominator of the second rational number: “;

    cin >> ratNum2;

 

    // Preparing the denominator of the second rational number

    int ratDen2 = 0;

    cout << “Enter the numerator of the second rational number: “;

    cin >> ratDen2;

 

    // Create our second rational number

    Rational rat2;

    rat2.setNumerator(ratNum2);

    rat2.setDenominator(ratDen2);

 

    // Sample arithmetic operation

    Rational rat3 = rat1 + rat2;

    cout << rat1.getNumerator() << “/” << rat1.getDenominator() << ” + “;

    cout << rat2.getNumerator() << “/” << rat2.getDenominator();

    cout << ” = ” << rat3.getNumerator() << “/” << rat3.getDenominator() << endl;

 

    // Sample simplification

    cout << “Simplified “ << rat3.getNumerator() << “/” << rat3.getDenominator() << ” is “;

    rat3.simplify();

    cout << rat3.getNumerator() << “/” << rat3.getDenominator() << endl;

 

    // Sample comparison operation

    if(rat1 > rat2)

    {

        cout << rat1.getNumerator() << “/” << rat1.getDenominator() << ” is bigger than “ << rat2.getNumerator() << “/” << rat2.getDenominator() << endl;

    }

    else

    {

        cout << rat1.getNumerator() << “/” << rat1.getDenominator() << ” is NOT bigger than “ << rat2.getNumerator() << “/” << rat2.getDenominator() << endl;

    }

    system(“PAUSE”);

    return 0;

}

 

void Rational::setNumerator(int n)

{

    num = n;

}

 

void Rational::setDenominator(int n)

{

    den = n;

}

 

int Rational::getNumerator()

{

    return num;

}

 

int Rational::getDenominator()

{

    return den;

}

 

// Greatest common divisor

int Rational::gcd()

{

    int a = num;

    int b = den;

    int tmp;
    // While b is not 0

    while (b)

    {

        tmp = b;

        b = a % b;

        a = tmp;

    }

    return a;

}

 

// Simplifies the fraction

void Rational::simplify()

{

    // Get the greatest common divisor

    int gcdNum = gcd();

    // If there is a common divisor, we don’t want to divide by 0

    if(gcdNum != 0)

    {

        // Set the new numerator

        num = num / gcdNum;

        // Set the new denominator

        den = den / gcdNum;

    }

}

 

Rational Rational::operator+(Rational ratPassed)

{

    // Adding fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den + den * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator-(Rational ratPassed)

{

    // Subtracting fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den – den * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator*(Rational ratPassed)

{

    // Multiplying fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.num;

    ratResult.den = den * ratPassed.den;

    return ratResult;

}

 

Rational Rational::operator/(Rational ratPassed)

{

    // Dividing fractions

    Rational ratResult;

    ratResult.num = num * ratPassed.den;

    ratResult.den = den * ratPassed.num;

    return ratResult;

}

 

bool Rational::operator==(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (ratPassed.num == num) && (ratPassed.den == den);

}

 

bool Rational::operator!=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (ratPassed.num != num) && (ratPassed.den != den);

}

 

bool Rational::operator>(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) > (ratPassed.num * den);

}

 

bool Rational::operator<(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) < (ratPassed.num * den);

}

 

bool Rational::operator>=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) >= (ratPassed.num * den);

}

 

bool Rational::operator<=(Rational ratPassed)

{

    // Simplify the first rational number

    simplify();

    // Simplify the second (passed) rational number

    ratPassed.simplify();

    return (num * ratPassed.den) <= (ratPassed.num * den);

}

 

Rational Rational::operator-()

{

    // Unary operator multiplies the denominator by -1

    Rational ratResult;

    ratResult.den = den * -1;

    return ratResult;

}

Nathan Pakovskie is an esteemed senior developer and educator in the tech community, best known for his contributions to Geekpedia.com. With a passion for coding and a knack for simplifying complex tech concepts, Nathan has authored several popular tutorials on C# programming, ranging from basic operations to advanced coding techniques. His articles, often characterized by clarity and precision, serve as invaluable resources for both novice and experienced programmers. Beyond his technical expertise, Nathan is an advocate for continuous learning and enjoys exploring emerging technologies in AI and software development. When he’s not coding or writing, Nathan engages in mentoring upcoming developers, emphasizing the importance of both technical skills and creative problem-solving in the ever-evolving world of technology. Specialties: C# Programming, Technical Writing, Software Development, AI Technologies, Educational Outreach

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top