简体   繁体   English

运算符使用ostream和链接进行重载。 为何选择返回?

[英]operator overloading using ostream and chaining. Why return by reference?

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 overloaded for an object MyObject) : 如果我们有(假设运算符已经为对象MyObject正确重载):

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

Now, there will be subexpressions like ((cout << obj1) << obj2)); 现在,会有像((cout << obj1)<< obj2))这样的子表达式; The question is why can we not return by value ? 问题是为什么我们不能以价值回报? (Ok, let's assume that it's allowed return ostream as value) If cout << obj1 return a stream object instead of a reference, what is the difference ? (好吧,我们假设它允许返回ostream作为值)如果cout << obj1返回一个流对象而不是一个引用,有什么区别? Why is chaining impossible then ? 为什么链接不可能呢? Just as with overloading of the '=' operator, we can't chain like A=B=C=D if we return by value. 就像'='运算符的重载一样,如果按值返回,我们就不能像A = B = C = D那样链。 Why ? 为什么?


Thank you for answers. 谢谢你的回答。 I realize that I can chain without return by reference, but my output is quite 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;
     }

The output from this is : 8,9 7,5 4,1 2,3 其结果是:8,9 7,5 4,1 2,3

But if I overload with return by reference and set the parameter as reference, and modify and return *this when overloading instead, I get : 2,3 2,3 2,3 2,3 但是,如果我通过引用返回重载并将参数设置为引用,并在重载时修改并返回* this,我得到:2,3 2,3 2,3 2,3

What is the reason no objects are altered in the first example ? 在第一个例子中没有对象被改变的原因是什么? Is it because of lvalues vs rvalues ? 是因为左值与右值? How about shorthand operators in comparison? 比较速记运营商怎么样?


Ok, another update. 好的,另一个更新。 As mentioned, the correct result should be 2,3 for all. 如上所述,正确的结果应该是2,3。 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 correct results. 然后,我会得到正确的结果。 (2,3 2,3 2,3 2,3). (2,3 2,3 2,3 2,3)。 What happens to *this ? *这会怎么样? The overloaded operator update the lhs with rhs in the overload function, but returning *this seem to be pointless. 重载的运算符在重载函数中用rhs更新lhs,但返回*这似乎没有意义。 Where does *this end up in : b3 = b4 = b2 = b1 ? *最终在哪里:b3 = b4 = b2 = b1? Will it try to return to the left, so that it actually returns nothing when the chain reaches b3 (That will try to return to the left)? 它是否会尝试返回左侧,以便当链达到b3时它实际上不返回任何内容(那将尝试返回到左侧)?

The main reason is because returning by value makes a copy, and iostream objects are not copyable. 主要原因是因为按值返回会生成副本,而iostream对象不可复制。 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. 它们具有状态和标识,并且不清楚复制它们应该意味着什么:对象包含(逻辑上,至少)它在流中的位置,所以如果我创建一个副本,我有两个对象将写在同一位置流。

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. b3将接受b4作为其rhs,但你实际上并没有修改b3的值,你正在创建一个相同类型Blah的新变量并将其返回null(在这种情况下,null表示什么都没有,因为没有任何内容左b3 ,即使有事情左侧b3 ,它不会有所作为的另一个Blah变量会做同样的事情, b3b4一样。

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. 事实上,如果你有另一个类(比如CoolClass也有x和y)并重载赋值运算符以接受一个blah变量并让它实际修改它自己的x和y你会发现它。

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 & 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. 这可以保证您的rhs不可变,并且链接行为正常。

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. 如果您正在寻找具有不同类型对象的更好行为,例如ostream,则声明友元函数有时会有所帮助。 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. 这种方法的好处是让一个看起来像来自ostream类的运算符,但它在Blah类中。 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. 这样做是传递相同的ostream对象并按优先顺序填充数据。 The exact same behavior you would expect to find with regular text and ostream. 与常规文本和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. 假设obj1obj2都是Blahcout对象通过friend函数接收obj1并返回由obj1的数据修改的相同cout对象,然后新修改的cout对象接受obj2再次返回相同的修改cout对象,但是现在它也被obj2修改了。

(cout << obj1) << obj2;
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) 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 . 通过调用(2),您将调用=()并在上面的代码片段(1)中返回一个临时对象。注意您没有更新实际对象的x值。例如,操作开始像这样b2(x ,y)= b1(2,3)并且你在临时对象ret中初始化值2和3并且按值返回临时对象。所以临时对象现在将调用b4,即b4 = temp(2,3)并且再次使用相同的基础。你将2和3复制到b4并返回一个临时对象,它调用b3为b3 = temp(2,3)。现在如果你替换(2)就像这个打印的cout << b3 = b4 = b2 = b1(提供工具<<重载),你将得到2和3作为输出因为2和3只能在这一行中使用。在你的例子中,你打印的是下一行,这个值不可用因为你没有在调用的对象中更新x和y的值。为了解决这个问题,你应该返回* this作为引用,这意味着你将返回调用该函数的对象。 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. 所以,如果你做b2 = b1并返回* this,即表示你正在返回实际对象和x,实际对象的y值正在更新,只要该对象存在,它就可用。

here is one interesting answer i founded on learncpp.com 这是我在learncpp.com上创建的一个有趣的答案

ostream is a class provided as part of C++ that handles outputs streams. ostream是作为C ++的一部分提供的类,用于处理输出流。 The details of how ostream is implemented is very complex, but fortunately also completely unnecessary to use it effectively. 如何实现ostream的细节非常复杂,但幸运的是,完全没有必要有效地使用它。

Since ostream is a class, ostream& is a reference to an ostream class. 由于ostream是一个类,因此ostream是对ostream类的引用。 Note that we're also taking an ostream& as a parameter. 请注意,我们也将采用ostream作为参数。 ostream is typically passed by reference because we don't want to make a copy of it as we pass it around. ostream通常通过引用传递,因为我们不想在传递它时复制它。

So basically, our overloaded function takes an ostream as a parameter, writes stuff to it, and then returns the same ostream. 基本上,我们的重载函数将ostream作为参数,将内容写入其中,然后返回相同的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. 首先解析cout < < cPoint1 ,cout成为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. 当这个重载函数写完out参数时,它返回那个cout,所以下一次调用<<可以使用它。 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. 在此函数结束时,再次返回cout。 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. 表达式结束,程序移动到下一行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM