Is it possible to improve operator overloading using references?

I am learning C++ and I'm using code from a text book (although I do some minor edits).
I have some questions about the code (mainly things like why not do this instead of that to improve it etc.) to better understand C++ and I'm going to put them as comments in the code. This way, it may be easier to convey the question.
Thanks in advance for your time and help.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Complex {
private:
    double re, img; // real and imaginary part
    
public:
    Complex(double real, double imaginary): re{real}, img{imaginary}{}
    Complex(double real): re{real}, img{0}{}
    Complex(): re{0}, img{0}{}
    
    double real() const {return this->re;}
    void real(double real) {this->re = real;}
    double imaginary() const {return this->img;}
    void imaginary(double imaginary) {this->img = imaginary;}
    
// Isn't it more efficient and as safe to change the signature of the following functions
// to something like "Complex & operator+=(const Complex & z)"?
    Complex & operator+=(Complex z) {this->re += z.re; this->img += z.img; return *this;}
    Complex & operator-=(Complex z) {this->re -= z.re; this->img -= z.img; return *this;}
    Complex & operator*=(Complex z) {this->re *= z.re; this->img *= z.img; return *this;}
    Complex & operator/=(Complex z) {this->re /= z.re; this->img /= z.img; return *this;}
    
    friend ostream & operator<<(ostream & o, Complex & c);
    
};

// Isn't it more efficient and as safe to change the signature of the following functions
// to something like "Complex & operator+(Complex a, const Complex & b)"?
Complex operator+(Complex a, Complex b) {return a+=b;}
Complex operator-(Complex a, Complex b) {return a-=b;}
Complex operator*(Complex a, Complex b) {return a+=b;}
Complex operator/(Complex a, Complex b) {return a/=b;}

Complex operator-(Complex a) {return {-a.real(), -a.imaginary()};}

// Isn't it more efficient and as safe to change the signature of the following functions
// to something like "bool operator==(Complex & a, Complex & b)"?
bool operator==(Complex a, Complex b) {
    return a.real() == b.real() && a.imaginary() == b.imaginary();
}
bool operator!=(Complex a, Complex b) {
    return !(a==b);
}


I know that the difference is minimal, since the Complex type itself doesn't hold much, but I want to know how it works so that I can put it to use in heavier classes.

Thanks again!
Last edited on
> I know that the difference is minimal, since the Complex type itself doesn't hold much,
> but I want to know how it works so that I can put it to use in heavier classes.

Yes, you are correct; it does not make much of a difference here, but in general, pass objects that need not be modified by reference to const.

Noter: for operator == and operator !=, the parameters should be of type const Complex&
// Isn't it more efficient and as safe to change the signature of the following functions
// to something like "Complex & operator+(Complex a, const Complex & b)"?

Almost, but the return type shouldn't be a reference, because you'd be returning a reference to local object, so it won't be valid once the function ends.
Thanks, @JLBorges! Got it!

@Ganando, Thank you for your reply!
I thought the same first, but was confused about why it worked after a test.
Here's the complete code with a test for the + operator at the end with a "Complex & operator+(Complex a, const Complex & b)" signature.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>

using namespace std;

class Complex {
private:
    double re, img; // real and imaginary part
    
public:
    Complex(double real, double imaginary): re{real}, img{imaginary}{}
    Complex(double real): re{real}, img{0}{}
    Complex(): re{0}, img{0}{}
    
    double real() const {return this->re;}
    void real(double real) {this->re = real;}
    double imaginary() const {return this->img;}
    void imaginary(double imaginary) {this->img = imaginary;}
    
    Complex & operator+=(const Complex & z) {this->re += z.re; this->img += z.img; return *this;}
    Complex & operator-=(Complex & z) {this->re -= z.re; this->img -= z.img; return *this;}
    Complex & operator*=(Complex & z) {this->re *= z.re; this->img *= z.img; return *this;}
    Complex & operator/=(Complex & z) {this->re /= z.re; this->img /= z.img; return *this;}
    friend ostream & operator<<(ostream & o, Complex & c);
    
};

Complex & operator+(Complex a, const Complex & b) {return a+=b;}
Complex operator-(Complex a, Complex b) {return a-=b;}
Complex operator-(Complex a) {return {-a.real(), -a.imaginary()};}
Complex operator*(Complex a, Complex b) {return a+=b;}
Complex operator/(Complex a, Complex b) {return a/=b;}

bool operator==(Complex a, Complex b) {
    return a.real() == b.real() && a.imaginary() == b.imaginary();
}
bool operator!=(Complex a, Complex b) {
    return !(a==b);
}

ostream & operator<<(ostream & o, Complex & c) {
    o << "(" << c.real() << "," << c.imaginary() << ")";
    return o;
}
/*
 * 
 */
int main(int argc, char** argv) {
    Complex a(10, 3);
    Complex b(4, 5);
    
    cout << a << "\n";
    cout << b << "\n";
    
    b += a;
    
    cout << a << "\n";
    cout << b << "\n";
    
    // This works but I don't know why?
    cout << "here's the plus operator at work" << "\n";
    Complex c = a + b;
    cout << a << "\n";
    cout << b << "\n";
    cout << c << "\n";
    return 0;
}



As I see it: a is passed by value and b by reference to Complex & operator+(Complex, const Complex &) which in turn calls Complex & operator+=(const Complex &) so at the end the copied a, as I understand, is return by reference by the first function. So, as you said, a local copy is returned by reference, but it works!!
How so?
Last edited on
This is what you are doing, rephrased:

int * notgoodanymore()
{
int x;
return &x; //uh oh. x is going to die in a moment.
}

so what happens? The address where X was still exists, and its on your program's stack (so, your program OWNS it and the OS will NOT complain about access violations). You can use it, but something else may have claimed all or part of the bytes there.... it can become corrupted, and you can corrupt something else. It may also be unused. If its unused, it will 'work fine' even if its a horrible bug. If its in use, it may 'work fine' with a subtle, maybe even unnoticed, bug.
Last edited on
Referencing an invalid object is undefined behavior. Undefined means anything can happen. Anything.

I get different results when I run your program on cpp.sh than when I run it on my local computer.
cpp.sh:
here's the plus operator at work
(10,3)
(14,8)
(10,3)


my machine (MinGW):
here's the plus operator at work
(10,3)
(14,8)
(24,11)


That being said... I can't actually figure out a way to get GCC to warn about this. Maybe I'm looking at it wrong.

Last edited on
Side-note:
friend ostream & operator<<(ostream & o, Complex & c);
1. Unnecessary friendship. That function does not access private parts of the Complex.

2. Lacking constness. You can't output const complexes with that even though the function uses only const members.
You guys are just amazing! Thank you so much for the insights and help.

Got the undefined part. I was thinking with a garbage collector present in my mind (LOL). So, it is totally unsafe to access that part of the memory, although it was giving me the right result. There is no guarantee.

Got that with the friend thing. Being friendly, I can access private members and if I don't, there is no need for being friendly.

2. Lacking constness. You can't output const complexes with that even though the function uses only const members.

@keskiverto, I don't get that second part of your statement. Can you please elaborate? Thanks!
1
2
const Complex x( 7 );
std::cout << x; // error: cannot bind non-const referenc to x that is const 

but
1
2
3
4
ostream & operator<<(ostream & o, const Complex & c);

const Complex y( 42 );
std::cout << y; // ok 
Wow, thanks! That was totally new to me.
So, the compiler doesn't allow me to pass a non-const object by const reference, but it is allowed the other way around. Makes total sense!

I don't want to embarrass myself by sounding too excited, but truth is, this is my first day here and you guys have just exceeded my expectations by miles. Thank you so much.
Topic archived. No new replies allowed.