为啥赋值运算符要返回对对象的引用?

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,我想这比我预期的要多。接受您指出复制和交换的答案 感谢您的回复。

以上是关于为啥赋值运算符要返回对对象的引用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥移动赋值运算符应该返回对 *this 的引用 [重复]

在Java中,如何返回对对象的引用,以便可以使用赋值运算符修改对象

c++中重载输出操作符,为啥要返回引用

为啥我们在赋值运算符重载中使用引用返回而不是在加减运算中?

赋值运算符重载函数 引用返回 与 对象返回

c++重载赋值操作符的返回值是啥?