有很多问题和答案,但我无法找到为什么我们需要通过引用返回。
如果我们有(假设操作符已经为对象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时它实际上不会返回任何内容(这将尝试返回到左侧)?
答案 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
变量与Blah
和b3
执行的操作相同。
事实上,如果你有另一个类(比如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
都是obj2
,Blah
对象通过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。但是没有人愿意使用它,所以忽略了返回值。表达式结束,程序移动到下一行。