【问题标题】: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;  
}

在我在网上找到的所有引用中,我注意到运算符返回对源对象的引用。 为什么返回对对象的引用而不是什么都没有的正确方法?

【问题讨论】:

标签: c++ operator-overloading return-type 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】:

    你的赋值运算符应该始终做这件事情:

    1. 将常量引用输入(const MyClass &rhs) 作为赋值的右侧。这样做的原因应该很明显,因为我们不想意外更改该值;我们只想更改左侧的内容。

    2. 始终返回对新更改的左侧return *this 的引用。这是为了允许运算符链接,例如a = b = c;

    3. 始终检查自分配(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,我想这比我预期的要多。接受您指出复制和交换的答案 感谢您的回复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 2012-04-23
    • 2019-01-27
    • 1970-01-01
    • 2011-08-05
    • 2011-10-16
    相关资源
    最近更新 更多