0

I've implemented operator+= (Rational) in my implementation file, yet I noticed on accident that Rational+= long long works, even though I haven't implemented that particular function.

The relevent function of my main is when I use plusequals += num.

plusequals is declared as Rational plusequals and num is declared as long long num. Both are initialized to contain a value from user input.

What gives? It would seem to me that this shouldn't work, but it does.

Here is my header file:

#ifndef _RATIONAL_H_
#define _RATIONAL_H_

#include<iostream>


using namespace std;

class Rational
{
long long _p;
long long _q;

void simplify();

public:
Rational();
Rational (long long p, long long Q = 1);
Rational (const Rational&);

Rational& operator= (const Rational&);
Rational& operator+= (const Rational&);
Rational& operator-= (const Rational&);
Rational& operator*= (const Rational&);
Rational& operator/= (const Rational&);

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

Rational operator+ (const Rational&);
Rational operator+ (long long) const;
friend Rational operator+ (long long, const Rational&);
Rational operator- (const Rational&);
Rational operator- (long long) const;
friend Rational operator- (long long, const Rational&);
Rational operator* (const Rational&);
Rational operator* (long long) const;
friend Rational operator* (long long, const Rational&);
Rational operator/ (const Rational&);
Rational operator/ (long long) const;
friend Rational operator/ (long long, const Rational&);

bool operator== (const Rational&) const;
bool operator== (long long) const;
friend bool operator== (long long, const Rational&);
bool operator!= (const Rational&) const;
bool operator!= (long long) const;
friend bool operator!= (long long, const Rational&);
bool operator> (const Rational&) const;
bool operator> (long long) const;
friend bool operator> (long long, const Rational&);
bool operator< (const Rational&) const;
bool operator< (long long) const;
friend bool operator< (long long, const Rational&);
bool operator>= (const Rational&) const;
bool operator>= (long long) const;
friend bool operator>= (long long, const Rational&);
bool operator<= (const Rational&) const;
bool operator<= (long long) const;
friend bool operator<= (long long, const Rational&);

Rational operator++ (int);
Rational operator-- (int);
Rational& operator++ ();
Rational& operator-- ();
Rational operator- () const;
Rational operator+ () const;

Rational pow (unsigned exp) const;
Rational inverse() const;
};

#endif

And here is the implementation:

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

void validate (long long, long long);

int gcd (long long, long long);

Rational::Rational()
{
    _p = 0;
    _q = 1;
}

Rational::Rational (long long p, long long Q)
{
    validate (p, Q);
    _p = p;
    _q = Q;
}

Rational::Rational (const Rational& rat)
{
    this->_p = rat._p;
    this->_q = rat._q;
}
void Rational::simplify()
{
    // Fixes negative denominators.
    if (_q < 0)
    {
        _p *= -1;
        _q *= -1;
    }

    // Simplifies Rational Numbers.
    int denom = gcd(_p, _q);
    _p /= denom;
    _q /= denom;

}

Rational& Rational::operator= (const Rational& rat)
{
    _p = rat._p;
    _q = rat._q;

    return *this;
}

Rational& Rational::operator+= (const Rational& rat)
{
    _p = ((_p * rat._q) + (_q * rat._p));
    _q *= rat._q;

    this->simplify();

    return *this;
}

Rational& Rational::operator-= (const Rational& rat)
{
    _p = ((_p * rat._q) - (_q * rat._p));
    _q *= rat._q;

    this->simplify();

    return *this;
}

Rational& Rational::operator*= (const Rational& rat)
{
    _p *= rat._p;
    _q *= rat._q;

    this->simplify();

    return *this;
}

Rational& Rational::operator/= (const Rational& rat)
{
    if (rat._p == 0)
    {
        throw "Division by zero not allowed";
    }
    _p *= rat._q;
    _q *= rat._p;

    this->simplify();

    return *this;
}

ostream& operator<< (ostream& os, const Rational& rat)
{
    os << rat._p << ":" << rat._q;

    return os;
}

istream& operator>> (istream& is, Rational& rat)
{
    long long p, q;

    is >> p >> q;
    validate(p, q);
    rat._p = p;
    rat._q = q;
    rat.simplify();

    return is;
}

Rational Rational::operator+ (const Rational& rat)
{
    Rational result(*this);

    result += rat;
    result.simplify();

    return result;
}

Rational Rational::operator+ (long long num) const
{
    Rational result(*this);
    Rational temp(num);

    result += temp;
    result.simplify();

    return result;
}

Rational operator+ (long long num, const Rational& rat)
{
    Rational result(num);
    result += rat;
    result.simplify();

    return result;
}

Rational Rational::operator- (const Rational& rat)
{
    Rational result(*this);

    result -= rat;
    result.simplify();

    return result;
}

Rational Rational::operator- (long long num) const
{
    Rational result(*this);
    Rational temp(num);

    result -= temp;
    result.simplify();

    return result;
}

Rational operator- (long long num, const Rational& rat)
{
    Rational result(num);
    result -= rat;
    result.simplify();

    return result;
}

Rational Rational::operator* (const Rational& rat)
{
    Rational result(*this);

    result *= rat;
    result.simplify();

    return result;
}

Rational Rational::operator* (long long num) const
{
    Rational result(*this);
    Rational temp(num);
    result *= temp;
    result.simplify();

    return result;
}

Rational operator* (long long num, const Rational& rat)
{
    Rational result(num);
    result *= rat;
    result.simplify();

    return result;
}

Rational Rational::operator/ (const Rational& rat)
{
    Rational result(*this);

    result /= rat;
    result.simplify();

    return result;
}

Rational Rational::operator/ (long long num) const
{
    Rational result(*this);
    Rational temp(num);

    result /= temp;
    result.simplify();

    return result;
}

Rational operator/ (long long num, const Rational& rat)
{
    Rational result(num);
    result /= rat;
    result.simplify();

    return result;
}

bool Rational::operator== (const Rational& rat) const
{
    bool result;

    if ((this->_p == rat._p) && (this->_q == rat._q))
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

bool Rational::operator== (long long num) const
{
    bool result;
    Rational temp(num);

    result = (*this == temp);

    return result;
}

bool operator== (long long num, const Rational& rat)
{
    bool result;

    result = (rat == num);

    return result;
}

bool Rational::operator!= (const Rational& rat) const
{
    return !(*this == rat);
}

bool Rational::operator!= (long long num) const
{
    return !(*this == num);
}

bool operator!= (long long num, const Rational& rat)
{
    return !(num == rat);
}

bool Rational::operator> (const Rational& rat) const
{
    bool result;

    if ((this->_p / this->_q) > (rat._p / rat._q))
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

bool Rational::operator> (long long num) const
{
    bool result;
    Rational temp(num);

    result = (*this > temp);

    return result;
}

bool operator> (long long num, const Rational& rat)
{
    bool result;

    result = (rat < num);

    return result;
}

bool Rational::operator< (const Rational& rat) const
{
    bool result;

    if (!(*this > rat) && !(*this == rat))
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

bool Rational::operator< (long long num) const
{
    bool result;
    Rational temp(num);

    result = (*this < temp);

    return result;
}

bool operator< (long long num, const Rational& rat)
{
    bool result;

    result = (rat > num);

    return result;
}

bool Rational::operator>= (const Rational& rat) const
{
    bool result;

    if (!(*this < rat))
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

bool Rational::operator>= (long long num) const
{
    bool result;
    Rational temp(num);

    result = (*this >= temp);

    return result;
}

bool operator>= (long long num, const Rational& rat)
{
    bool result;

    result = (rat <= num);

    return result;
}

bool Rational::operator<= (const Rational& rat) const
{
    bool result;

    if (!(*this > rat))
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

bool Rational::operator<= (long long num) const
{
    bool result;
    Rational temp(num);

    result = (*this <= temp);

    return result;
}

bool operator<= (long long num, const Rational& rat)
{
    bool result;

    result = (rat >= num);

    return result;
}

Rational Rational::operator++ (int) // Postfix
{
    Rational temp(*this);

    this->_p++;
    this->_q++;

    return temp;
}

Rational Rational::operator-- (int) // Postfix
{
    Rational temp(*this);

    this->_p--;
    this->_q--;

    return temp;
}

Rational& Rational::operator++()
{
    this->_p++;
    this->_q++;

    return *this;
}

Rational& Rational::operator--()
{
    this->_p--;
    this->_q--;

    return *this;
}

Rational Rational::operator-() const
{
    Rational temp(-(this->_p), (this->_q));

    return temp;
}

Rational Rational::operator+() const
{
    Rational temp(+(this->_p), +(this->_q));

    return temp;
}

Rational Rational::pow (unsigned exp) const
{
    Rational result(*this);
    Rational temp(*this);

    if (exp == 0)
    {
        result = 1;
    }
    else
    {
        for (unsigned i = 1; i < exp; i++)
        {
            result *= temp;
        }
    }

    return result;
}

Rational Rational::inverse() const
{
    Rational temp(this->_q, this->_p);

    return temp;
}

void validate(long long p, long long q)
{
    p++; // Supress error for unused value. Decided to keep value in parameter list to maintain clarity.
    if (q == 0)
    {
        throw "Zero Denominator";
    }
}

int gcd(long long p, long long q)
{
    // Euclid's Algorithm
    if (q == 0)
    {
        return p;
    }
    return gcd (q, p%q);
}

And for good measure, the test main() I'm using:

#include <string>
#include <iostream>
#include "Rational.h"

int main()
{
    while (true)
    {
        Rational rat1;
        Rational rat2;
        Rational rat3;
        long long num;
        unsigned exp;
        Rational power(rat1);
        string hello = "hello";

        // Get input
        try
        {
            cout << "Please enter a numerator and denominator separated by a space: ";
            cin >> rat1;
            cout << endl;
            cout << "Please enter a second numerator and denomintator seperated by a space: ";
            cin >> rat2;
            cout << endl;
            cout << "Please enter a numerator and denominator separated by a space for a third Rational number: ";
            cin >> rat3;
            cout << endl;
            cout << "Enter a number to use for arithmetic operations: ";
            cin >> num;
            cout << endl;
            cout << "Please enter a positive integer to use an exponent :";
            cin >> exp;
            cout << endl;
        }
        catch (char const* err)
        {
            cerr << err << " - Non-Zero denominators only ya big goon.\n";
        }

        cout << endl;

        cout << "You put: " << rat1 << " and: " << rat2 << endl;

        Rational plusequals (rat1);
        Rational minusequals (rat1);
        Rational timesequals (rat1);
        Rational divequals (rat1);

        plusequals += rat2;
        minusequals -= rat2;
        timesequals *= rat2;
        try
        {
            divequals /= rat2;
        }
        catch (const char* msg)
        {
            cerr << msg << endl;
        }

        cout << "+= : " << plusequals << "\n-= : " << minusequals << "\n*= : " << timesequals << "\n/= : " << divequals << endl;

        plusequals = rat1;
        minusequals = rat1;
        timesequals = rat1;
        divequals = rat1;

        plusequals += num;
        minusequals -= num;
        timesequals *= num;
        try
        {
            divequals /= num;
        }
        catch (const char* msg)
        {
            cerr << msg << endl;
        }

        cout << "\nRational = " << rat1<< ", num = " << num << "  :\n";
        cout << rat1 << " += " << num << ": " << plusequals << endl << rat1 << " -= " << num << ": " << minusequals << endl << rat1 <<" *= " << num << ": " << timesequals << endl << rat1 << " /= " << num << ": " << divequals << endl;

        plusequals = rat1;
        minusequals = rat1;
        timesequals = rat1;
        divequals = rat1;

        plusequals += rat3;
        minusequals -= rat3;
        timesequals *= rat3;
        try
        {
            divequals /= rat3;
        }
        catch (const char* msg)
        {
            cerr << msg << endl;
        }

        cout << "\nRational = " << rat1<< ", Rational = " << rat3 << "  :\n";
        cout << rat1 << " += " << rat3 << ": " << plusequals << endl << rat1 << " -= " << rat3 << ": " << minusequals << endl << rat1 << " *= " << rat3 << ": " << timesequals << endl << rat1 << " /= " << rat3 << ": " << divequals << endl;


        power = rat1.pow(exp);
        cout << endl << rat1 << " raised to the power of " << exp << " : ";
        cout << power << endl;

        cout << "The multiplicative inverse of " << rat1 << " is " << rat1.inverse() << endl;

        // Comparison
        cout << endl << endl;
        if (rat1 == rat2)
        {
            cout << rat1 << " = " << rat2 << endl;
        }
        if (rat1 != rat2)
        {
            cout << rat1 << " != " << rat2 << endl;
        }
        if (rat1 <= rat2)
        {
            cout << rat1 << " <= " << rat2 << endl;
        }
        if (rat1 >= rat2)
        {
            cout << rat1 << " >= " << rat2 << endl;
        }
        if (rat1 < rat2)
        {
            cout << rat1 << " < " << rat2 << endl;
        }
        if (rat1 > rat2)
        {
            cout << rat1 << " > " << rat2 << endl;
        }
    }
}

Also, I should note that I know I shouldn't be using namespace std; in my header files... but we're required to copy the header directly from our professor. He also insists that we use the bracket style that I'm using in this particular project, which I think is ugly but it is what it is. He's very particular. Any other improvements/insights into the assignment would be much appreciated, though.

Rama
  • 3,222
  • 2
  • 11
  • 26
Joseph Morgan
  • 210
  • 2
  • 8
  • 4
    You should try to cut down to only the relevent code here. 90% is irrelevant to the problem. Or, at the very least, summarize the relevant bits at the top. – Carcigenicate Mar 03 '17 at 13:45
  • You perhaps have your `long long` implicitly converted to `Rational`. Make your second constructor `explicit` to prevent that from happening. – Resurrection Mar 03 '17 at 13:48
  • Can't `+=` be defined in terms of `+` and `=`? You defined a `long long` overload of `+`. It's probably using that. – Carcigenicate Mar 03 '17 at 13:50
  • @Carcigenicate **No**. C++ operator overloading doesn't work that way. `+` and `+=` are two distinct operators and there's no way to implicitly define one using the other. It can (and often is) be done manually, but it's not a part of the language. – Angew is no longer proud of SO Mar 03 '17 at 13:56
  • Related: https://stackoverflow.com/questions/15077466/what-is-a-converting-constructor-in-c-what-is-it-for – Baum mit Augen Mar 03 '17 at 14:08
  • Possible duplicate: https://stackoverflow.com/questions/4766964/how-can-the-assignment-from-int-to-object-be-possible-in-c Getting warmer... – Baum mit Augen Mar 03 '17 at 14:13
  • @Angew Whoops. Thanks for the clarification. – Carcigenicate Mar 03 '17 at 16:04

3 Answers3

1
Rational_object += (long long)_object

works because you have a converting constructor.

Rational (long long p, long long Q = 1);

Which makes it possible for the compiler implicitly convert long long to a Rational. You can prevent this behavior by marking such constructor explicit

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
1

long long implicitly converters into Rational because your class has Rational (long long p, long long Q = 1); constructor. You can use explicit keyword for avoiding implicitly casting.

Community
  • 1
  • 1
JustRufus
  • 492
  • 1
  • 5
  • 10
1

To answer your question about +=: look at this constructor:

Rational (long long p, long long Q = 1);

The constructor is not decorated with the explicit keyword, which means it can be used for implicit conversions.

What happens when you have a += expression with operands of type Rational and long long is that the compiler searches for all definitions of operator += and then looks if any of them can accept the operand types (that's overload resolution).

If none match directly (they don't in your case), it will then try to see whether any implicit conversions can make them match. Your += requires Rational on both sides. The left-hand side operation is already a Rational, nothing needed there. The right-hand side is long long, but an implicit conversion from long long to Rational exists (via the constructor). So the compiler issues code to create a temporary Rational from the long long, and then feeds this temporary Rational into your operator +=.

You could prevent these implicit conversions by marking the constructor as explicit, but I think you'd be wrong to do so. After all, is there any problem with writing "1/2 + 1 = 3/2"? We don't need to write "1/2 + 1/1".

Implicit conversions between numbers are useful. 3.14 + 1 is perfectly valid C++ (and perfectly reasonable) thanks to an implicit conversion from int to double.


Regarding your other request,

Any other improvements/insights into the assignment would be much appreciated, though.

that's not really on-topic for Stack Overflow. Assuming you code works, you might get interesting feedback on Code Review.

Community
  • 1
  • 1
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Thanks a ton for your reply and correcting me on my improvements request. I wasn't aware of Code Review, I'll definitely check it out. – Joseph Morgan Mar 15 '17 at 23:31