4

对此有很多问题和答案,但我真的找不到为什么我们需要通过引用返回。

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

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

现在,会有像 ((cout << obj1) << obj2)); 这样的子表达式。问题是为什么我们不能按值返回?(好吧,假设它允许将 ostream 作为值返回)如果 cout << 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

第一个示例中没有更改对象的原因是什么?是因为 lvalues vs rvalues 吗?相比之下,速记运算符怎么样?


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

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

然后,我会得到正确的结果。(2,3 2,3 2,3 2,3)。*this 会发生什么?重载运算符在重载函数中用 rhs 更新 lhs,但返回 *this 似乎毫无意义。*this 在哪里结束: b3 = b4 = b2 = b1 ?它会尝试返回左侧,因此当链到达 b3 时它实际上什么也不返回(这将尝试返回左侧)?

4

5 回答 5

8

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

于 2013-02-18T11:52:31.040 回答
2

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

于 2013-02-18T11:45:07.650 回答
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;---->(2)

通过调用 (2) 您将调用 =() 并且在上面的代码片段 (1) 中,您将返回一个临时对象。请注意,您不会更新实际对象的 x 值。例如,操作像这样开始 b2(x ,y) = b1(2,3) 并且您正在初始化临时对象 ret 中的值 2 和 3 并按值返回临时对象。因此临时对象现在将调用 b4 即 b4 = temp(2,3 ) 和同样的funda。你将复制 2 & 3 到 b4 并返回一个临时对象,它调用 b3 作为 b3 = temp(2,3)。现在如果你替换 (2) 像这样打印 cout << b3=b4 =b2=b1(提供实现<<重载),您将获得 2 & 3 作为输出,因为 2 & 3 仅在此行中可用。在您的示例中,您将在下一行打印此值不可用,这仅是因为您没有更新调用的对象中 x & y 的值。因此,要解决此问题,您应该返回 *this 作为参考,这意味着如果您执行 b2=b1 并返回 *this,您将返回调用函数的对象。也就是说,您正在返回实际对象,并且实际对象的 x,y 值正在更新,只要对象存在。

于 2013-08-22T13:31:19.023 回答
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;

b3b4作为它的 rhs,但您实际上并没有修改 的值b3,而是创建了一个相同类型的新变量Blah并将其返回为 null(在这种情况下,null 意味着什么,因为b3.即使 的左边有什么东西,b3也不会有什么不同,因为另一个Blah变量会做同样的事情b3并且b4做。

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

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

我仍然不确定您对通过引用传递的主要抱怨是什么。这是我为 Blah 编写该运算符的方法。

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

这保证了您的 rhs 是不可变的并且链接行为正常工作。

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

friend std::ostream & Blah::operator << (std::ostream & lhs, const Blah & rhs)
{ return lhs << "{" << rhs.x << ", " << rhs.y << "}"; }

它的作用是传递同一个 ostream 对象并按优先顺序用数据填充它。您期望在常规文本和 ostream 中找到完全相同的行为。

使用您的第一个示例,您可以这样想。假设obj1obj2都是Blahcout对象obj1通过朋友函数接收并返回cout由 中的数据修改的相同对象obj1,然后新修改的cout对象再次obj2返回相同的修改cout对象,但现在它也被修改了obj2

(cout << obj1) << obj2;
于 2013-02-18T14:46:40.330 回答
0

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

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

由于 ostream 是一个类,因此 ostream& 是对 ostream 类的引用。请注意,我们还将 ostream& 作为参数。ostream 通常通过引用传递,因为我们不想在传递它时复制它。

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

cout << cPoint1 << cPoint2 << cPoint3;

这解决如下:

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

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

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

变成:

(cout << cPoint2) << cPoint3;

变成:

cout << cPoint3;

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

于 2014-04-19T18:17:58.920 回答