tags:

views:

160

answers:

5

I'm wondering if you can overload an operator and use it without changing the object's original values.

Edited code example:

class Rational{
public:
   Rational(double n, double d):numerator_(n), denominator_(d){};
   Rational(){}; // default constructor
   double numerator() const { return numerator_; } // accessor
   double denominator() const { return denominator_; } // accessor
private:
    double numerator_;
    double denominator_;
};

const Rational operator+(const Rational& a, const Rational& b)
{
      Rational tmp;
      tmp.denominator_ = (a.denominator() * b.denominator());
      tmp.numerator_ = (a.numerator() * b.denominator());
      tmp.numerator_ += (b.numerator() * a.denominator());
      return tmp;
}

I made the accessors const methods, but I'm still getting a privacy error for every tmp.denominator_ / numerator_.

+3  A: 

no .. you have to rewrite + and - .. it's not that hard. Then change the signatures to

const Rational Rational::operator+(Rational& num) const
const Rational Rational::operator-(Rational& num) const

that way the compiler will let you know if you are modifying your object.

eduffy
RHS should also be const.
Martin York
+5  A: 

Maybe I'm missing something, but why don't you just take out the code that modifies the arguments?

const Rational Rational::operator+(Rational& num)
{
      Rational tmp;
      tmp.denominator_ = (denominator_*num.denominator_);
      //numerator_*=num.denominator_;
      //num.numerator_*=denominator_;
      tmp.numerator_ = (numerator_+num.numerator_);
      return tmp;
}

This would be caught earlier by being const-correct.

That means your function signature should be this:

Rational Rational::operator+(const Rational& num) const

Then you will get errors because you are modifying const objects. The way your operators are written now is generally considered incorrect.

When you add 2 + 3, neither 2 nor 3 changes: they are const.

Edit

Sorry, I missed the actual math part. Here are a few things:

As a member function (what I have above), do this:

// Note that I use lhs and rhs to refer to the left-hand
// and right-hand sides of an operation. As a member function
// my left-hand side is implicitly `this`.
Rational Rational::operator+(const Rational& rhs) const
{
    Rational temp;

    temp.denominator_ = (denominator() * rhs.denominator());
    temp.numerator_ = (numerator() * rhs.denominator());
    temp.numerator_ += (denominator() * rhs.numerator());
    return temp;
}

As a global function, do this:

Rational operator+(const Rational& lhs, const Rational& rhs)
{
    Rational temp;

    temp.denominator_ = (lhs.denominator() * rhs.denominator());
    temp.numerator_ = (lhs.numerator() * rhs.denominator());
    temp.numerator_ += (lhs.denominator() * rhs.numerator());

    return temp;
}

The issue here is you'll get access violations: your member variables are private to the class. You need to let the compiler know that it is okay if this function handles your class's private variables by making the function a friend of the class:

class Rational
{
public:
    friend Rational operator+(const Rational& lhs, const Rational& rhs);
}
GMan
You have to multiply the numerator by the denominator. It's adding fractions.
trikker
+3  A: 

What you're looking for are the "binary" addition and subtraction operators:

const Rational operator+(const Rational& A, const Rational& B)
{
    Rational result;

    ...

    return result;
}

update (in response to new code and comments):

You are getting that error because your accessor functions are not declared as constant functions, so the compiler has to assume that they might modify the original object. Change your accessors as follows, and you should be good to go:

double numerator() const { return numerator_; }
double denominator() const { return denominator_; }

update

To properly handle privacy issues, you should declare the binary operator+ function as a friend of the Rational class. Here is how it would look:

class Rational {
public:
    Rational(double n, double d):numerator_(n), denominator_(d) {};
    Rational() {}; // default constructor
    double numerator() const { return numerator_; } // accessor
    double denominator() const { return denominator_; } // accessor
    friend Rational operator+(const Rational& A, const Rational& B);
private:
    double numerator_;
    double denominator_;
};

const Rational operator+(const Rational& a, const Rational& b)
{
    Rational result;

    result.denominator_ = (a.denominator_ * b.denominator_);
    result.numerator_ = (a.numerator_ * b.denominator_);
    result.numerator_ += (b.numerator_ * a.denominator_);

    return result;
}
e.James
Still incorrect. The parameters should be const.
GMan
@GMan: Thank you for catching that. Fixed.
e.James
Yes I actually changed it and made the operators global and used the accessors to get the values, but I still can't assign to the temporary object. i"ll post the code.
trikker
@trikker: You just need to declare your accessors as `const` functions. I edited my answer with the details.
e.James
Yup, just figured that out. Still having privacy issues though due to the tmp object.
trikker
You should make the `operator+` function a `friend` of the `Rational` class. That will give it access to the private members.
e.James
+2  A: 

Since your class already provides accessors to the numerator and denominator, and it has a public constructor, there is no need to use any friends. You can overload operator+ as follows:

const Rational operator+(const Rational& rhs, const Rational& lhs)
{
    double newNumerator = rhs.numerator() * lhs.denominator() +
                          rhs.denominator() * lhs.numerator();
    return Rational(newNumerator, rhs.denominator() * lhs.denominator());
}

In response to some of the other answers:

Everything about this question is exactly answered by Item 24 of Effective C++ (Third Edition). In this item, Scott Meyers shows that, in cases dealing with numerical types where you want to support implicit conversion in an intuitive manner, it is best to use a non-member non-friend function.

Say your Rational class had the following constructor:

Rational::Rational(double numerator = 0, double denominator = 1);

In this case, if operator+ were a member function, trying to do mixed mode arithmetic would work only half the time:

Rational oneHalf(1, 2);
oneHalf + 2;   // works
2 + oneHalf;   // error!

In the second example, operator+ for integers is called. The way to fix this is to make operator+ for Rationals a non-member function as shown above.

If you have access to Effective C++, you should also check out Item 23: Prefer non-member non-friend functions to member functions.

Whisty
A: 

I prefer to implement operator+ in terms of operator+=. Then clients get to choose if they want a temp copy or not. Also operator+= is more natural as a member function so it has access to the private parts.

class Rational {
public:
    Rational(double n, double d):numerator_(n), denominator_(d) {};
    Rational() {}; // default constructor

    double numerator() const { return numerator_; } // accessor
    double denominator() const { return denominator_; } // accessor

    Rational & operator+=(const Rational& b) {
     denominator_ *= b.denominator_);
     numerator_ *= (b.denominator_);
     numerator_ += (b.numerator_ * denominator_);
     return *this;
    }

private:
    double numerator_;
    double denominator_;
};


const Rational operator+(const Rational& a, const Rational& b)
{
    Rational result(a);
    result += b;
    return result;
}

However the easiest way to implement operator overloading is to use the boost operators library. Then you implement the minimum set of operators and boost take care of the rest and all the gotchas (including wintermute's example of 2 + oneHalf.

iain