Cartino
Cartino

Reputation: 41

How does operator overloading work, and why doesn't it work in my case?

I've been provided a driver function that is supposed to demonstrate the results of operator overloading involving complex numbers. After reading up on overloading a while I managed to write the code in a way that it successfully compiles, however somewhere along the way the correct values are not being outputted by the program.

From what I understand overloading essentially works like a function. Objects are passed and the "function" can then do arithmetic/whatever with it and return a new object. Where I'm slightly lost though, is how overloading knows what values are being passed. For example, in my case I overloaded the "+" and "=" operators in order to add two complex numbers in the form "x = y + z". When the compiler comes across the "=" symbol, I'm assuming it simply passes whatever is on both the left and right side and passes those? Same with "+". In this case it would be passing "y" since it's the object on the left and "z" because it's the object on the right?

Here's my current "complex" class which includes the overloading definitions.

class Complex {

private:
double realPart;
double imaginaryPart;

public:
// friends
friend ostream & operator<<(ostream &out, const Complex &c);
friend istream & operator>>(istream &in,  Complex &c);

// constructors
Complex()
{
    realPart = 0;
    imaginaryPart = 0;
}

Complex(double real)
{
    realPart = real;
    imaginaryPart = 0;
}

Complex(double real, double imaginary)
{
    realPart = real;
    imaginaryPart = imaginary;
}
// end of constructors

// + overloading
Complex operator+(Complex const &c)
{
    Complex Add;
    Add.realPart = realPart + c.realPart;
    Add.imaginaryPart = imaginaryPart + c.imaginaryPart;
    return Add;
}

// - overloading
Complex operator-(Complex const &c)
{
    Complex Subtract;
    Subtract.realPart = realPart - c.realPart;
    Subtract.imaginaryPart = imaginaryPart - c.imaginaryPart;
    return Subtract;
}

// * overloading
Complex operator*(Complex const &c)
{
    Complex Multiply;
    Multiply.realPart = (realPart * c.realPart) - (imaginaryPart * c.imaginaryPart);
    Multiply.imaginaryPart = (realPart * c.imaginaryPart) - (imaginaryPart * c.realPart);
    return Multiply;
}

// = overloading
Complex operator=(Complex const &c)
{
    Complex Assignment;
    Assignment.realPart = realPart;
    Assignment.imaginaryPart = imaginaryPart;
    return Assignment;
}

// == overloading
bool operator==(Complex const &c)
{
    Complex Compare;
    if (Compare.realPart == realPart && Compare.imaginaryPart == imaginaryPart)
    {
        return true;
    }
    else
    {
        return false;
    }
}

    // != overloading
bool operator!=(Complex const &c)
{
    Complex NotEqual;
    if (NotEqual.realPart == realPart && NotEqual.imaginaryPart == imaginaryPart)
    {
        return false;
    }
    else
    {
        return true;
    }
}

};

// << overloading
ostream& operator<<(ostream& out, const Complex &c)
{
    out << c.realPart;
    if (c.imaginaryPart >= 0)
    {
        out << " + " << c.imaginaryPart << "i" << endl;
    }
    else
    {
        out << " - " << fabs (c.imaginaryPart) << "i" << endl;
    }

    return out;
}

// >> overloading
istream& operator>>(istream &in, Complex &c)
{
    in >> c.realPart;
    in >> c.imaginaryPart;
    return in;
}

And here's the driver program:

int main()
{
    for (double i = 1; i < 10; ++ i)
    {
        Complex y{i * 2.7, i + 3.2};
        Complex z{i * 6, i + 8.3};

        Complex x;
        Complex k;

        std::cout << "Enter a complex number in the form: (a, b)\n? ";
        std::cin >> k; // demonstrating overloaded >>
        std::cout << "x: " << x << "\ny: " << y << "\nz: " << z << "\nk: " << k << '\n'; // demonstrating overloaded <<

        x = y + z; // demonstrating overloaded + and =
        std::cout << "\nx = y + z:\n" << x << " = " << y << " + " << z << '\n';
        x = y - z; // demonstrating overloaded - and =
        std::cout << "\nx = y - z:\n" << x << " = " << y << " - " << z << '\n';
        x = y * z; // demonstrating overloaded * and =
        std::cout << "\nx = y * z:\n" << x << " = " << y << " * " << z << "\n\n";

        if (x != k)
        { // demonstrating overloaded !=
            std::cout << x << " != " << k << '\n';
        }

        std::cout << '\n';
        x = k;

        if (x == k)
        {
            // demonstrating overloaded ==
            std::cout << x << " == " << k << '\n';
        }

        std::cout << std::endl;
}
}

Upon running, the problem seems to be with the object "x". Entering "5 2" will still output "x: 0 + 0i" This leads me to believe the issue is with either overloading of "=" or the stream operators. That said, I can't quite figure out why nothing is happening.

Is there an error in how I've constructed the "=" overloading definition as I think, or is it maybe something bigger I'm missing?

Upvotes: 0

Views: 169

Answers (2)

Francis Cugler
Francis Cugler

Reputation: 7895

The operator=() is not correct: user Yakk - Adam has already shown you how to fix it. To give you insight on why it is wrong and what return *this does; let's look at your original function:

Complex operator=(Complex const &c) {
    Complex Assignment;
    Assignment.realPart = realPart;
    Assignment.imaginaryPart = imaginaryPart;
    return Assignment;
}

Here your signature takes a const reference to another Complex object this part is correct. Your return type is a Complex object this is in essence wrong because you don't want to return a copy of an object. The objective here is to perform assignment. This means you must make changes to the original LHS instance.

In the expression A = B + C; A is considered the LHS instance. Here you want to assign the expression (B + C) which are both RHS values.

So when Yakk - Adam showed you how to fix this by:

Complex& operator=(Complex const &c) {
  realPart = c.realPart;
  imaginaryPart = c.imaginaryPart;
  return *this;
}

One of the differences here is that the return type is now a Reference to a specific object instead of a copy to an object.

Another difference is that there is no need to create a local temporary copy as you did in your original version:

Complex Assignment; // this is not needed

By removing that from operator=() he simply replaced these lines of code:

// Assignment.realPart = realPart; // To
realPart = c.realPart;
// Assignment.imaginaryPart = imaginaryPart; // To
imaginaryPart = c.imaginaryPart;

Here you are using the class's members directly and assigning to them the value that belongs to the other or c that is passed to the operator.

Then finally to return your LHS instance with the updated value; this is where you have to return a dereferenced this pointer.

What does *this mean? The this pointer is a special pointer that belongs to all class and struct types. For example any time you have class object:

class Foo {
public:
    int bar { 10 };

    void addFive();
}; 

You can use the this pointer directly in your member functions:

void Foo::addFive() {
    this->bar += 5; // same as below  (this) belongs to this particular instance.
    // bar += 5;        
}

Regarding your operator=(); since you are returning by reference you can not simply just return this. This will return the this pointer. We don't want a pointer to the object as we want a reference to the object; so we must deference the this pointer by returning *this.

I hope this helps clear things up for you.

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275220

Your = is wrong; it should return *this.

Complex& operator=(Complex const &c)
{
  realPart = c.realPart;
  imaginaryPart = c.imaginaryPart;
  return *this;
}

Fix that and most of the rest looks sane.

Upvotes: 3

Related Questions