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

时间:2013-02-18 11:43:36

标签: c++ operator-keyword ostream

有很多问题和答案,但我无法找到为什么我们需要通过引用返回。

如果我们有(假设操作符已经为对象MyObject正确重载):

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

现在,会有像((cout&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; obj2))的子表达式; 问题是为什么我们不能按价值返回? (好吧,我们假设它允许返回ostream作为值)如果cout&lt;&lt; obj1返回一个流对象而不是一个引用,有什么区别?为什么链接不可能呢?就像'='运算符的重载一样,如果按值返回,我们就不能像A = B = C = D那样链。为什么?


谢谢你的回答。我意识到我可以通过引用链接而不返回,但是当重载'='时我的输出完全不同。如果我写:

    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;
     }

这个输出是: 8,9 7,5 4,1 2,3

但是如果我通过引用返回重载并将参数设置为引用,并在重载时修改并返回* this,我得到: 2,3 2,3 2,3 2,3

第一个例子中没有对象被改变的原因是什么? 是因为左值与右值?比较速记运营商怎么样?


好的,另一个更新。如上所述,正确的结果应该是2,3。但是,如果我将重载运算符写为:

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

然后,我会得到正确的结果。 (2,3 2,3 2,3 2,3)。 *这会怎么样?重载的运算符在重载函数中用rhs更新lhs,但返回*这似乎没有意义。 *最终在哪里:b3 = b4 = b2 = b1?它是否会尝试返回左侧,以便当链达到b3时它实际上不会返回任何内容(这将尝试返回到左侧)?

5 个答案:

答案 0 :(得分:6)

主要原因是因为按值返回会产生副本,而且 iostream对象不可复制。他们有州和 身份,并不清楚复制它们应该是什么意思: 对象包含(逻辑上,至少)它在中的位置 流,所以如果我创建一个副本,我有两个对象 写在流中的相同位置。

答案 1 :(得分:2)

按值返回不会阻止链接。但是,如果按价值返回,则会返回副本,在这种情况下通常不是您想要的。

答案 2 :(得分:1)

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将以b4作为其rhs,但实际上并没有修改b3的值,而是创建了一个相同类型的新变量{{1并将其返回null(在这种情况下,null意味着什么,因为Blah左边没有任何内容。即使b3左侧有某些内容,也不会产生任何影响另一个b3变量与Blahb3执行的操作相同。

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

b4

我仍然不确定您通过引用传递的主要投诉是什么。以下是我为Blah编写运算符的方法。

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

这可以保证你的rhs不可变,链接行为也能正常工作。

如果您正在寻找具有不同类型对象的更好行为,例如ostream,那么声明友元函数有时会有所帮助。这些函数可以在新类中声明,但不能直接属于该类。这种方法的好处是让一个看起来像来自ostream类的运算符,但它在Blah类中。您仍然可以在友元函数中使用私有和受保护的成员,这使它们变得有用。

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

这样做是传递相同的ostream对象并按优先顺序填充数据。与普通文本和ostream相同的行为。

使用你的第一个例子,你可以这样想。假设friend std::ostream & Blah::operator << (std::ostream & lhs, const Blah & rhs) { return lhs << "{" << rhs.x << ", " << rhs.y << "}"; } obj1都是obj2Blah对象通过friend函数接收cout并返回由obj1修改的cout对象obj1中的数据,然后新修改的cout对象接收obj2将再次返回相同的已修改cout对象,但现在它也被{{1}修改也是。

obj2

答案 3 :(得分:1)

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; ----&gt;(2)

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

答案 4 :(得分:0)

这是我在learncpp.com上创建的一个有趣的答案

ostream是一个作为C ++的一部分提供的类,用于处理输出流。 ostream如何实现的细节非常复杂,但幸运的是,完全没有必要有效地使用它。

因为ostream是一个类,ostream&amp;是对ostream类的引用。请注意,我们也采取了ostream&amp; amp;作为参数。 ostream通常是通过引用传递的,因为我们不想在传递它时复制它。

基本上,我们的重载函数将ostream作为参数,将内容写入其中,然后返回相同的ostream。这允许我们将<<个调用链接在一起:

cout << cPoint1 << cPoint2 << cPoint3;

这解决如下:

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

cout < < cPoint1首先得到解决,cout成为ostream&amp;参数。当这个重载函数完成写入out参数时,它返回该cout,以便下一次调用<<可以使用它。因此:

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

变为:

(cout << cPoint2) << cPoint3;

变为:

cout << cPoint3;

这最后一次调用我们的重载函数。在此函数结束时,再次返回cout。但是没有人愿意使用它,所以忽略了返回值。表达式结束,程序移动到下一行。