There are many questions and answers for this, but I can\'t really find why we need to return by reference.
If we have (assume operator is already correctly ov
Returning by value does not prevent chaining. But if you return by value, you're returning a copy, which is generally not what you want in this case.
Blah Blah::operator =(Blah rhs){
Blah ret;
ret.x = rhs.x;
ret.y = rhs.y;
return ret;
}
Blah b1(2,3);
Blah b2(4,1);
Blah b3(8,9);
Blah b4(7,5);
b3 = b4 = b2 = b1;
b3
is going to take in b4
as its rhs, but you are not actually modifying the value of b3
, you are making a new variable of the same type Blah
and returning it to null (in this case null means nothing because there is nothing to the left of b3
. Even if there was something to the left of b3
, it would not make a difference as another Blah
variable would do the same thing as b3
and b4
did.
In fact if you had another class (say CoolClass that also has and x and y) and overloaded the assignment operator to take in a blah variable and have it actually modify the its own x and y you would find that.
coolObject = b3 = b4 = b2 = b1; //CoolObject.x = 2, CoolObject.y = 3
I'm still not exactly sure what your major complaint is with passing by reference. Here is how I would write that operator for Blah.
Blah & Blah::operator = (const Blah & rhs) { x = rhs.x; y = rhs.y; return *this; }
This guarantees that your rhs is non mutable and that chaining behavior works properly.
If you are looking for better behavior with a different kind of object, say the ostream for instance, it can sometimes be helpful to declare a friend function. These are functions that you can declare in your new class, but don't belong to the class directly. The benefit of this approach is to have a operator that looks like it comes from ostream class, but it is in the Blah class instead. You can still use private and protected members inside a friend function which makes them useful.
friend std::ostream & Blah::operator << (std::ostream & lhs, const Blah & rhs)
{ return lhs << "{" << rhs.x << ", " << rhs.y << "}"; }
What this does is pass around the same ostream object and fills it with data in order of precedence. The exact same behavior you would expect to find with regular text and ostream.
Using your first example you can think of it this way. Assuming obj1
and obj2
are both Blah
, the cout
object takes in obj1
via the friend function and returns the same cout
object modified by the data in obj1
, then the newly modified cout
object takes in obj2
are returns the same modified cout
object again, but now its also being modified by obj2
as well.
(cout << obj1) << obj2;
here is one interesting answer i founded on learncpp.com
ostream is a class provided as part of C++ that handles outputs streams. The details of how ostream is implemented is very complex, but fortunately also completely unnecessary to use it effectively.
Since ostream is a class, ostream& is a reference to an ostream class. Note that we’re also taking an ostream& as a parameter. ostream is typically passed by reference because we don’t want to make a copy of it as we pass it around.
So basically, our overloaded function takes an ostream as a parameter, writes stuff to it, and then returns the same ostream. This allows us to chain <<
calls together:
cout << cPoint1 << cPoint2 << cPoint3;
This resolves as follows:
((cout << cPoint1) << cPoint2) << cPoint3;
cout < < cPoint1
is resolved first, with cout becoming the ostream& parameter. When this overloaded function is finished writing to the out parameter, it returns that cout so the next call to <<
can use it. Thus:
((cout << cPoint1) << cPoint2) << cPoint3;
becomes:
(cout << cPoint2) << cPoint3;
becomes:
cout << cPoint3;
This calls our overloaded function one last time. At the end of this function, cout is again returned. But there's nobody left to use it, so the return value is ignored. The expression ends, and the program moves to the next line.
Blah Blah::operator =(Blah rhs){
Blah ret;
ret.x = rhs.x;
ret.y = rhs.y;
return ret;
}-->(1)
Blah b1(2,3);
Blah b2(4,1);
Blah b3(8,9);
Blah b4(7,5);
b3 = b4 = b2 = b1;---->(2)
By invoking (2) you will call =() and Here in the above snippet(1) you are returning a temporary object .Note that you are not updating the x value of the actual object.For eg operation starts like this b2(x,y) = b1(2,3) and you are initialising the value 2 & 3 in the temporary object ret and returning the temporary object by value .so that temporary object is now going to invoke b4 ie b4 = temp(2,3) and again the same funda.you will copy 2 & 3 to b4 and returns a temp object which is invoking b3 as b3 = temp(2,3).Now if you replace (2) like this printed cout << b3=b4=b2=b1 (provided implement << overloading),you would have got 2 & 3 as output because 2 & 3 will be avilable only in this line.In your example you are printing in the next lines where this value is not available only because you are not updating the value of x & y in the object which invoked .So for that to work out you should return *this as a reference which means you are returning the object which invoked the function .SO if you do b2=b1 and return *this ,ie means you are returning the actual object and x,y value of actual object is getting updated and it will be available as long as the object exists.
The main reason is because returning by value makes a copy, and iostream objects are not copyable. They have state and identity, and it's not clear what copying them should mean: the object contains (logically, at least) its position in the stream, so if I create a copy, I have two objects which will write at the same position in the stream.