Reputation: 19
I'm trying to overload the binary + operator in C++. Now, I figured that it is wrong to return a reference to an Object when overloading operators because although the reference still exists when the method ends, the object will be deleted. So, this is wrong:
Vec& operator+(Vektor& a)
{
Vec temp(*this);
temp.x = this->x + a.x;
temp.y = this->y + a.y;
temp.z = this->z + a.z;
return temp;
}
This would be correct:
Vec operator+(Vektor& a)
{
Vec temp(*this);
temp.x = this->x + a.x;
temp.y = this->y + a.y;
temp.z = this->z + a.z;
return temp;
}
Now, my question is, if I actually use the upper version, why does the output of c work and the direct output of the result doesnt? I overloaded the << operator aswell. The second output results in gibberish, like 1.9492387e-12 or something. The first output (c) correctly gives me 2, 4 and 6.
Vec* a = new Vec(1, 2, 3);
Vec* b = new Vec(1, 2, 3);
Vec c = (*a + *b);
std::cout << c << std::endl << (*a + *b) << std::endl;
Any ideas? Here is the overloaded << operator:
friend std::ostream& operator<<(std::ostream& o, Vektor& a) {
o << a.x << std::endl << a.y << std::endl << a.z << std::endl;
return o;
}
Also, why do I return a reference to the streams here?
Thanks.
Upvotes: 1
Views: 77
Reputation: 43662
In the first case
Vec& operator+(Vec& a)
{
Vec temp(*this);
temp.x = this->x + a.x;
temp.y = this->y + a.y;
temp.z = this->z + a.z;
return temp;
}
you are creating a temporary object "temp" and returning its reference. There isn't much optimizations like RVO can do since you're explicitly asking for a reference to that specific object (they can't take place).. and after this function ends you'll have a reference to garbage. This is undefined behavior, you might still get right results but you might not. As for "why c works and the direct computation doesn't" it's not possible to know a priori, perhaps the direct computation has some sort of temporary value involved that immediately gets reused and thus you see garbage while the first does not.
In the second case, as you noted, you're asking for a copy of that object, and thus receiving a copy of that object which has the values copied in the right place.
I used the code below (C++11):
#include <iostream>
using namespace std;
class Vec {
public:
Vec operator+(Vec& a)
{
Vec temp(*this);
temp.x = this->x + a.x;
temp.y = this->y + a.y;
temp.z = this->z + a.z;
return temp;
}
friend std::ostream& operator<<(std::ostream& o, Vec a) {
o << a.x << std::endl << a.y << std::endl << a.z << std::endl;
return o;
}
int x,y,z;
};
int main() {
Vec* a = new Vec{1, 2, 3};
Vec* b = new Vec{1, 2, 3};
Vec c = (*a + *b);
std::cout << c << std::endl << (*a + *b) << std::endl;
return 0;
}
Notice that the reference in the friend function to output the contents of the vector works because the object you're returning is the same you passed as parameter by reference and is fully functional (std::cout
)
Upvotes: 0
Reputation: 227618
This here is undefined behaviour:
Vec c = (*a + *b);
because the addition operator returns a reference to a defunct object. It may appear to work, but cannot be relied on. It is often said that when a program has undefined behaviour, literally anything can happen. This is an exaggeration, but the program can fail in unpredictable ways, and seem to work only sometimes. The bottom line is that it is wrong.
Concerning this
std::ostream& operator<<(std::ostream& o, Vektor& a)
the ostream
is returned by reference such that you can chain it, for example
std::cout << Vektor(1, 2, 3) << " " << Vektor(4, 5, 6) << std::endl;
The returned reference is to the input parameter.
Note that there is absolutely no reason to use dynamic allocation here:
Vec* a = new Vec(1, 2, 3);
You can simplify things by saying
Vec a(1, 2, 3);
Upvotes: 1