4

Here is the output:

First Complex Number:
Enter real part of complex number: 3
Enter imaginary part of complex number: 6

Second Complex Number:
Enter real part of complex number: 5
Enter imaginary part of complex number: -5

a == (-27.00+36.00i)
b == (5.00-5.00i)
a+b == (-22.00+31.00i)
a-b == (-32.00+41.00i)
a*b == (45.00+315.00i)
a*a == (-567.00-1944.00i)
b*b == (0.00-50.00i)
a*a (using postincrement) ==(-27.00+36.00i)

As you can see, not everything involving a is wrong, because it takes the square of a (a complex number) as a. So, while the answer to "a*a (using postincrement) == (-27.00+36.00i) is the correct answer...the part where it says "a == (-27.00+36.00i)" is incorrect, as it should be a==(3+6i). I believe the error lies within the overloading and friend aspects of my code, but I'm not sure how to fix it as I'm not given any errors...this is a problem of the logic in my code.

Here is my code:

#include<iostream>
#include<iomanip>
using namespace std;

class ComplexNum
{
public:
    ComplexNum(float = 0.0, float = 0.0); //default constructor that uses default arg. in case no init. are in main
    ComplexNum& getComplexNum(); //get real and imaginary numbers from keyboard
    ComplexNum& sum(ComplexNum a, ComplexNum b); //method to add two ComplexNum numbers together
    ComplexNum& diff(ComplexNum a, ComplexNum b); //method to find the difference of two complex numbers
    ComplexNum& prod(ComplexNum a, ComplexNum b); //method to find the product of two complex numbers
    ComplexNum& square(ComplexNum a); //method to find square using pre/post increment operators

    //overloaded operators
    ComplexNum& operator =  (const ComplexNum& that) = default;
    ComplexNum& operator += (const ComplexNum& that) { return sum(*this, that); }
    ComplexNum& operator -= (const ComplexNum& that) { return diff(*this, that); }
    ComplexNum& operator *= (const ComplexNum& that) { return prod(*this, that); }
    ComplexNum& operator ++() { return square(*this); } //called for ++num
    ComplexNum& operator ++(int) { return square(*this); } //called for num++

    ostream& print(ostream& stm = cout) const;


private:
    float real; //float data member for real number (to be entered in by user)
    float imaginary; //float data member for imaginary number (to be entered in by user)

    //non-member overloaded operators
    //a is passed by value
    friend ComplexNum operator+ (ComplexNum a, const ComplexNum& b) { return a += b; }
    friend ComplexNum operator- (ComplexNum a, const ComplexNum& b) { return a -= b; }
    friend ComplexNum operator* (ComplexNum a, const ComplexNum& b) { return a *= b; }
    friend ComplexNum operator++(ComplexNum a) { return a++; }

    friend ostream& operator<< (ostream& stm, const ComplexNum& c) { return c.print(stm); }
};

ComplexNum::ComplexNum(float a, float b)
{
    real = a;
    imaginary = b;
}

ComplexNum& ComplexNum::getComplexNum()
{
    ComplexNum keyboard;
    cout << "Enter real part of complex number: ";
    cin >> real;

    cout << "Enter imaginary part of complex number: ";
    cin >> imaginary;

    return keyboard; 
}

ComplexNum& ComplexNum::square(ComplexNum a)
{
    this->real = (a.real * a.real) - (a.imaginary * a.imaginary);
    this->imaginary = (2 * (a.real * a.imaginary));
    return *this;
}

ComplexNum& ComplexNum::sum(ComplexNum a, ComplexNum b)
{
    this->real = a.real + b.real;
    this->imaginary = a.imaginary + b.imaginary;
    return *this;
}

ComplexNum& ComplexNum::diff(ComplexNum a, ComplexNum b)
{
    this->real = a.real - b.real;
    this->imaginary = a.imaginary - b.imaginary;
    return *this;
}

ComplexNum& ComplexNum::prod(ComplexNum a, ComplexNum b)
{
    this->real = (a.real * b.real) - (a.imaginary * b.imaginary);
    this->imaginary = (a.real * b.imaginary) + (b.real * a.imaginary);
    return *this;
}

ostream& ComplexNum::print(ostream& stm) const
{
    return stm << "(" << noshowpos << real << showpos << imaginary << "i)";
}

int main()
{
    ComplexNum a, b;
    cout << "First Complex Number:" << endl;
    a.getComplexNum();
    cout << endl;
    cout << "Second Complex Number:" << endl;
    b.getComplexNum();
    cout << endl;
    cout << fixed << setprecision(2)
        << "a == " << a << '\n'
        << "b == " << b << '\n'
        << "a+b == " << a + b << '\n'
        << "a-b == " << a - b << '\n'
        << "a*b == " << a*b << '\n'
        << "a*a == " << a*a << '\n'
        << "b*b == " << b*b << '\n'
        << "a*a (using postincrement) ==" << a++ << '\n';
        cout << endl;

    system("PAUSE");
}
garyoak
  • 41
  • 3
  • `ComplexNum& getComplexNum(); ` -- Where is this function? – PaulMcKenzie Feb 12 '17 at 22:13
  • Investigate the term "sequence points." Then split up your long cout chain into at least 2 statements. – Avi Berger Feb 12 '17 at 22:13
  • 1
    Using `a++` breaks all the other usages of `a` in the output. – Bo Persson Feb 12 '17 at 22:16
  • @PaulMcKenzie I deleted irrelevant parts of the code...but I will post the whole thing in immediately for review. Thanks again. – garyoak Feb 12 '17 at 22:20
  • But @BoPersson didn't I overload the ++ operator to refer to square function? – garyoak Feb 12 '17 at 22:21
  • @garyoak -- When is `a++` invoked? I think this is the point you're missing. You could have had the same behavior if those were simple `int` variables instead of `ComplexNum`. – PaulMcKenzie Feb 12 '17 at 22:23
  • @AviBerger..sequence points just blew my mind...thank you! – garyoak Feb 12 '17 at 22:23
  • 1
    @gary - The order of evaluation of the parameters to `cout` (lots of them!) is unspecified, so you cannot tell when the increment occurs. – Bo Persson Feb 12 '17 at 22:25
  • I see Paul, having it all invoked within the same cout is consistent since a will be a++ – garyoak Feb 12 '17 at 22:25
  • `getComplexNum()` returns a reference to a local variable. – Christopher Oicles Feb 13 '17 at 02:24
  • 1
    @garyoak You do have a mistake in the `getComplexNum()` as pointed out by the previous comment. You're returning a reference to a local variable, which is undefined behavior. So basically, any output you get is bogus, even if you fix the `++` issue. Just return a `ComplexNum` by value instead of reference. – PaulMcKenzie Feb 13 '17 at 11:36
  • @AviBerger, this is not about sequence points. Each overloaded operator is a function call, so they are sequenced one after the other (i.e. in C++03 terminology, there are sequence points between each call). It's only the _order_ of calls to those functions that is unspecified. – Jonathan Wakely Feb 17 '17 at 00:28
  • 1
    Prefix-++ should return by reference, and postfix-++ return by value (you mess this up twice) – M.M Feb 17 '17 at 02:24
  • @JonathanWakely, I stand corrected. However, what is unspecified is the order of evaluating the arguments to the calls. I think this is more apparent if rewritten to function call notation with the hidden this parameter explicit. Roughly: cout << a << a++; => op<<( &( op<<(&cout, a) ), a++ ); – Avi Berger Feb 17 '17 at 04:26
  • @AviBerger I agree function call notation makes it clearer, but your "hidden this parameter" doesn't help, because it shouldn't exist: those functions are not member functions so they have no `this` parameter! It should be `operator<<(operator<<(cout, a), a++)` (which is valid C++ not just pseudocode) – Jonathan Wakely Feb 17 '17 at 10:01
  • @JonathanWakely, Oops. You're entirely correct. I seem to be being rather dense currently. – Avi Berger Feb 17 '17 at 17:47

1 Answers1

1

This is not about operator overloading, it's about order of evaluation.

In the statement:

cout << fixed << setprecision(2)
        << "a == " << a << '\n'
        << "b == " << b << '\n'
        << "a+b == " << a + b << '\n'
        << "a-b == " << a - b << '\n'
        << "a*b == " << a*b << '\n'
        << "a*a == " << a*a << '\n'
        << "b*b == " << b*b << '\n'
        << "a*a (using postincrement) ==" << a++ << '\n';

The arguments will be printed left-to-right, as you expect, but the individual steps to compute each argument do not have to be done in that order.

The compiler can compute the value of each sub-expression in any order, as long as the value is "ready" when it needs to be printed out. So it can do something like this:

auto&& temp1 = a + b;
auto&& temp2 = a - b;
auto&& temp3 = a*b;
auto&& temp4 = a*a;
auto&& temp5 = b*b;
auto&& temp6 = a++;
cout << fixed << setprecision(2)
        << "a == " << a << '\n'
        << "b == " << b << '\n'
        << "a+b == " << temp1 << '\n'
        << "a-b == " << temp2 << '\n'
        << "a*b == " << temp3 << '\n'
        << "a*a == " << temp4 << '\n'
        << "b*b == " << temp5 << '\n'
        << "a*a (using postincrement) ==" << temp6 << '\n';

If that happened, you would get exactly the behaviour you expect, because the sub-expressions would be evaluated in order, left-to-right. But the current C++ standard (C++14) allows the compiler to re-order the evaluations, which might allow it to optimise the code better, if it needs less stack space of fewer registers. Another valid execution of the statement would be:

auto&& temp1 = a++;
auto&& temp2 = b*b;
auto&& temp3 = a*a;
auto&& temp4 = a*b;
auto&& temp5 = a - b;
auto&& temp6 = a + b;
cout << fixed << setprecision(2)
        << "a == " << a << '\n'
        << "b == " << b << '\n'
        << "a+b == " << temp6 << '\n'
        << "a-b == " << temp5 << '\n'
        << "a*b == " << temp4 << '\n'
        << "a*a == " << temp3 << '\n'
        << "b*b == " << temp2 << '\n'
        << "a*a (using postincrement) ==" << temp1 << '\n';

This time you can see that the a++ operation happens first, and so the later calculations will be done after that has happened.

The compiler could also choose any other ordering of the sub-expression evaluations, e.g. it could evaluate a*a then ++a then a-b etc. (but in practice the most common orders are left-to-right and right-to-left)


As an aside, there are two other serious problems with your code:

This function returns a reference to a local variable:

ComplexNum& ComplexNum::getComplexNum()
{
    ComplexNum keyboard;
    // ...
    return keyboard; 
}

That local variable no longer exists after the function returns, so any attempt to use the reference accesses an object which doesn't exist. Never do this! Your compiler should have warned you there was a problem here, enable your compiler's warnings and do not ignore them!

Secondly, it's a really bad idea to overload an operator like ++ to mean something totally different like multiplying an object by itself. You should only use operator overloading when the operator makes sense for the operation you want to perform. Incrementing is not squaring, so it's a bad choice. I would just define a normal (non-operator) function for that.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521