为啥赋值运算符要返回对对象的引用?
Posted
技术标签:
【中文标题】为啥赋值运算符要返回对对象的引用?【英文标题】:Why should the assignment operator return a reference to the object?为什么赋值运算符要返回对对象的引用? 【发布时间】:2012-02-22 17:41:10 【问题描述】:我正在对我的 C++ 进行一些修改,并且我正在处理运算符重载,特别是“=”(赋值)运算符。我在网上看,遇到了多个讨论它的话题。在我自己的笔记中,我把我所有的例子都记下来了
class Foo
public:
int x;
int y;
void operator=(const Foo&);
;
void Foo::operator=(const Foo &rhs)
x = rhs.x;
y = rhs.y;
在我在网上找到的所有引用中,我注意到运算符返回对源对象的引用。 为什么返回对对象的引用而不是什么都没有的正确方法?
【问题讨论】:
正确的方式是实现你想要的语义的任何方式; 惯用方式当然是返回T&
(在您的示例中为Foo&
)。
@MooingDuck,我想我的问题用错了。我一直在假设我的笔记是错误的,但想知道为什么比正确的更多。
assignment operator return a reference to *this in C++ 的可能副本;还有Returning *this with an assignment operator
【参考方案1】:
当你重载一个运算符并使用它时,编译时真正发生的事情是这样的:
Foo a, b, c;
a = b;
//Compiler implicitly converts this call into the following function call:
a.operator=(b);
所以你可以看到 FOO 类型的对象 b 作为参数传递给对象 a 的相同类型的赋值函数。现在考虑一下,如果您想级联分配并执行以下操作:
a = b = c;
//This is what the compiler does to this statement:
a.operator=(b.operator=(c));
通过引用将对象作为参数传递给函数调用会很有效,因为我们知道不这样做我们会通过值传递,这会在对象的函数内部进行复制,这需要时间和空间。
语句 'b.operator=(c)' 将在此语句中首先执行,如果我们重载运算符以返回对当前对象的引用,它将返回对对象的引用:
Foo &operator=(const Foo& rhs);
现在我们的声明:
a.operator=(b.operator=(c));
变成:
a.operator(Foo &this);
其中“this”是对执行“b.operator=(c)”后返回的对象的引用。对象的引用在此处作为参数传递,编译器不必创建返回对象的副本。
如果我们没有让函数返回 Foo 对象或其引用,而是让它返回 void:
void operator=(const Foo& rhs);
语句会变成这样:
a.operator=(void);
这会引发编译错误。
TL;DR 您将对象或对对象的引用返回到级联(链)分配,即:
a = b = c;
【讨论】:
【参考方案2】:你的赋值运算符应该始终做这三件事情:
将常量引用输入(const MyClass &rhs)
作为赋值的右侧。这样做的原因应该很明显,因为我们不想意外更改该值;我们只想更改左侧的内容。
始终返回对新更改的左侧return *this
的引用。这是为了允许运算符链接,例如a = b = c;
。
始终检查自分配(this == &rhs)
。当您的类自己分配内存时,这一点尤其重要。
MyClass& MyClass::operator=(const MyClass &rhs)
// Check for self-assignment!
if (this == &rhs) // Same object?
return *this; // Yes, so skip assignment, and just return *this.
... // Deallocate, allocate new space, copy values, etc...
return *this; //Return self
【讨论】:
检查自我分配是一种幼稚的解决方案,正确的是复制和交换。 感谢您的回复,但我只是想通过省略自我分配检查来做一个简单的例子。我理解除了返回参考之外的一切。 @MatteoItalia 复制和交换可能很昂贵。例如,如果使用复制和交换,将一个大向量分配给另一个大向量就不能重用目标的内存。【参考方案3】:当您只在这样的语句中执行单个赋值时,返回类型无关紧要:
x = y;
当你这样做时,它开始变得重要:
if ((x = y))
...当你这样做时真的很重要:
x = y = z;
这就是您返回当前对象的原因:允许以正确的关联性链接分配。这是一个很好的常规做法。
【讨论】:
我不明白你为什么说“这很重要”。要么重要,要么不重要。能详细点吗? @balajeerc:英语中的“它开始很重要”的意思是“在后一种情况下很重要,但在前一种情况下不重要”。换句话说,“当从情况 A 变为 B 时,重要性('重要')从零变为非零”。在直接分配中,回报无关紧要。在条件中,您返回的内容是真还是假很重要,但不完全是它是哪个对象。在链式赋值的情况下,你真的希望返回是当前对象,否则结果会违反直觉。【参考方案4】:通常的形式返回对目标对象的引用以允许赋值链接。否则无法做到:
Foo a, b, c;
// ...
a = b = c;
不过,请记住正确分配运算符is tougher than it might seem。
【讨论】:
从来不知道复制和交换部分。我总是检查自赋值、赋值和返回 void,我想这比我预期的要多。接受您指出复制和交换的答案 感谢您的回复。以上是关于为啥赋值运算符要返回对对象的引用?的主要内容,如果未能解决你的问题,请参考以下文章