Operator overloading using ostream and chaining. Why follow a link?

There are many questions and answers to this, but I can't seem to find why we need to return from a link.

If we have (assuming the operator is already properly overloaded for the MyObject):

    MyObject obj1;
    MyObject obj2;
    cout << obj1 << obj2;

      

There will now be subexpressions like ((cout <obj1) <obj2)); The question is, why can't we return at cost? (Okay, let's say it allowed the return of ostream as a value). If cout <obj1 returns a stream object instead of a reference, what's the difference? Why is chaining impossible? As with overloading the '=' operator, we cannot chaining like A = B = C = D if we return by value. Why?


Thanks for answers. I understand that I can chain without returning by reference, but my output is very different when overloading '='. If I write:

    class Blah{
    public:
       Blah();
       Blah(int x, int y);
       int x;
       int y;
       Blah operator =(Blah rhs);
     };
     Blah::Blah(){}
     Blah::Blah(int xp, int yp){
       x = xp;
       y = yp;
     }
     Blah Blah::operator =(Blah rhs){
       Blah ret;
       ret.x = rhs.x;
       ret.y = rhs.y;
       return ret;
     }
    int main(){

      Blah b1(2,3);
      Blah b2(4,1);
      Blah b3(8,9);
      Blah b4(7,5);
      b3 = b4 = b2 = b1;
      cout << b3.x << ", " << b3.y << endl;
      cout << b4.x << ", " << b4.y << endl;
      cout << b2.x << ", " << b2.y << endl;
      cout << b1.x << ", " << b1.y << endl;
          return 0;
     }

      

Way out of this: 8.9 7.5 4.1 2.3

But if I overload with a return by reference and set the parameter as a reference, then change and return * this on overload, I get: 2.3 2.3 2.3 2.3

What is the reason why the objects are not changed in the first example? Is it because of lvalues ​​vs rvalues? How about shorthand operators in comparison?


Okay, one more update. As mentioned, the correct result should be 2.3 for everyone. However, if I write the overloaded operator as:

     Blah Blah::operator =(Blah rhs){
       x = rhs.x;
       y = rhs.y;
       return *this;
     }

      

Then I will get the correct results. (2.3, 2.3, 2.3, 2.3). What's going on with this? The overloaded operator updates lhs with rhs in the overload function, but it returns * it seems pointless. Where * is this in the end: b3 = b4 = b2 = b1? Will it try to go back to the left, so that it actually returns nothing when the chain reaches b3 (which will try to go back to the left)?

+3


source to share


5 answers


The main reason is that return by value makes a copy and iostream objects cannot be copied. They have an identity, and it's not clear what copying them should mean: the object (logically at least) contains its position in the stream, so if I create a copy, I have two objects that will write in the same place in stream.



+6


source


Returning by value does not prevent chaining. But if you return by value, you are returning a copy, which is usually not what you want in this case.



+2


source


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

will accept b4

as its rhs, but you don't actually change the value b3

, you create a new variable of the same type Blah

and return it to null (in this case, null means nothing, because there is nothing left to the left of b3

. Even if there was something to the left from b3

, it would not change, because other variable Blah

would have done the same thing and b3

, and b4

.

In fact, if you had a different class (say CoolClass, which also has x and y), and overloaded the assignment operator to take the blah variable and whether it actually changed its own x and y, you will find this.

coolObject = b3 = b4 = b2 = b1; //CoolObject.x = 2, CoolObject.y = 3

      

I'm still not entirely sure what your main complaint is, passing by the link. This is how I will write this operator for Blah.

Blah & Blah::operator = (const Blah & rhs) { x = rhs.x; y = rhs.y; return *this; }

      

This will ensure that your rhs is not changed and that the chaining behavior is working correctly.

If you are looking for better behavior with a different kind of object, say for example ostream, it can sometimes be helpful to declare a friend function. These are functions that can be declared in a new class, but do not directly relate to the class. The advantage of this approach is to have an operator that looks like it comes from the ostream class, but is in the Blah class. You can use private and protected members inside the 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 loop around the same ostream object and populate it with data in order of priority. The exact behavior you would expect to find with plain text and ostream.

Using your first example, you can think of it this way. Assuming that obj1

and obj2

are how, the object cout

accepts obj1

via the friend function and returns the same object cout

modified by the data in obj1

, then the newly modified cout

object accepts obj2

, returns the same modified object cout

, but now it also changes to obj2

.

(cout << obj1) << obj2;

      

+1


source


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 calling (2), you are calling = (), 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 example, startup starts like this b2 (x, y) = b1 (2,3) and you initialize the values ​​2 and 3 in a temporary object ret and return the temporary object by value. As this temporary object will now call b4 i.e. b4 = temp (2,3) and again the same file. You will copy 2 and 3 to b4 and return a temp object which calls b3 as b3 = temp (2,3). Now if you replace (2) like this printed cout <<b3 = b4 = b2 = b1 (assuming overloading is implemented), you would get 2 and 3 as output, because 2 and 3 would only be available on that line ... In your example, you are printing on the following lines where this value is not available just becausethat you are not updating the x and y value in the called object. So for that you have to return * this as a reference, which means you are returning an object which is called by the .SO if you do b2 = b1 and return * this, meaning you are returning an actual object and x, y is a value the actual object is updated and it will be available as long as the object exists.

+1


source


here is one interesting answer that I based on learncpp.com

ostream is a class provided as part of C ++ that handles output streams. The details of how ostream works are very complex, but fortunately also completely unnecessary to use effectively.

Since ostream is a class, ostream & is a reference to the ostream class. It should be noted that they also accepted ostream & as a parameter. ostream is usually passed by reference because we don't want to copy it when we pass it.

So our overloaded function takes ostream as a parameter, writes stuff to it, and then returns the same stream. This allows us to connect calls <<

:

cout << cPoint1 << cPoint2 << cPoint3;

      

This is resolved as follows:

((cout << cPoint1) << cPoint2) << cPoint3;

      

cout < < cPoint1

resolved first when cout becomes an ostream & parameter. When this overloaded function is finished writing the out parameter, it returns this cout, so the next call <<

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 returned again. But nobody left it, so the return value is ignored. The expression ends, and the program continues to the next line.

0


source







All Articles