【问题标题】:Why does operator = return *this?为什么 operator = 返回 *this?
【发布时间】:2016-04-06 09:30:19
【问题描述】:

假设我想覆盖operator =,这样我就可以做类似的事情

Poly p1;  // an object representing a polynomial
Poly p2;  // another object of the same type    
p2 = p1;  // assigns all the contents of p1 to p2

然后在我的operator = 的实现中,我有这样的东西:

Poly& Poly::operator=(const Poly &source) {
    // Skipping implementation, it already works fine…
    return *this;
}

不要介意实现,它已经可以正常工作了。

我担心的是当你return *this 时会发生什么?我知道它返回对对象的引用,但这是怎么回事?

p2 = &p1

【问题讨论】:

  • 由于I am just new to C++ 部分在一次编辑中丢失了,并且由于p2 = &p1 的变体在cmets 中重新出现... Poly& Poly::operator= 声明中的&意味着操作符返回一个引用 - 在这种情况下是对自身的引用,这正是 return *this; 所做的。另一方面,&p1 中的& 计算为p1 的地址,这是一个Poly* 指针,不能分配给Poly 对象。不要混淆& 在两种不同上下文中的两种含义。

标签: c++ assignment-operator copy-assignment


【解决方案1】:

return *this 这样你就可以编写普通的复合 C++ = 语句,例如:

Poly p1; //an object representing a polynomial
Poly p2;
Poly p2;

// ...

p3 = p2 = p1;  //assigns all the contents of p1 to p2 and then to p3

因为那句话基本上是:

p3.operator=(p2.operator=(p1));

如果p2.operator=(...) 没有return *this,那么您将没有任何意义可以传递给p3.operator=(...)

【讨论】:

  • 是的,我知道。但我担心的是当你返回 *this 时会发生什么?我的意思是,根据你的例子,它是这样工作的吗? p3 = p2 = &p1; p3 = &p2;
  • @JudeMaranga 相应地编辑了答案。你不想使用p1 的地址( &p1),operator= 需要一个const Poly &source 所以*this 给你。
  • 我明白你在里面想说什么。但是在 p3.operator=(...) return *this 之后,谁会得到这个返回值?
  • @JudeMaranga 就像42; 是一个有效的 C++ 语句一样。价值被丢弃。想想cout << "C++ throws unused " << "return values away" << endl;
  • @JudeMaranga 这就是return *this 的唯一原因。所以是的,如果你永远不会使用嵌套赋值,你不需要。但这是一个约定,最好坚持。如果你不知道,你会在未来的某个时候被咬。
【解决方案2】:

p2 = p1p2.operator=(p1) 的简写。它只是调用您的 operator= 函数,它返回对 p2 的引用,然后您将忽略它。为了清楚起见,我们称之为assign 而不是operator=

Poly& Poly::assign(const Poly &source) {
    .
    .
    .
    return *this;
}

现在你可以写而不是p2 = p1

p2.assign(p1);

在这种情况下,调用assign 的结果将被忽略,但您不必忽略它。例如,你可以这样写:

p3.assign(p2.assign(p1));

使用operator= 而不是assign,这就变成了

p3 = (p2 = p1);

但由于赋值是右结合的,这也可以写成

p3 = p2 = p1;

这种能够一次执行多个赋值的形式最初来自 C,并且通过在 operator=() 中返回 *this 的约定保留在 C++ 中。

【讨论】:

  • 返回的引用被忽略了?那么,如果返回值无论如何都会被忽略,为什么不让它无效呢?
  • @JudeMaranga:我已经扩展了我的答案以帮助解释。
  • @ChristianHackl:我做了一个小编辑。感谢您让我知道发生了什么。
  • @VaughnCato 为什么不返回一个常量引用呢?不是更安全吗?
【解决方案3】:

如果您无论如何都不需要链式分配(如其他答案所示),可能会试图让复制分配运算符返回void。毕竟,链式作业通常难以阅读和理解,因此不允许它们可能被视为一种改进

然而,一个经常被忽视的方面是void operator=(Poly& const) 意味着您的类型将不再满足CopyAssignable concept,这需要T& 返回类型。

不符合CopyAssignable 概念的类型不能正式用于某些标准容器操作,例如std::vector::insert,这意味着以下看似无辜的代码会产生未定义的行为,即使它可能运行完全没问题:

#include <vector>

struct Poly
{
    void operator=(Poly const&) {} // Poly is not CopyAssignable
};

int main()
{
  std::vector<Poly> v;
  Poly p;
  v.insert(v.begin(), p); // undefined behaviour
}

正如 C++ 标准在第 17.6.4.8/2.3 节中解释的那样,它讨论了使用标准库的程序的约束:

(...) 在以下情况下效果是未定义

(...) 用于在实例化 a 时用作模板参数的类型 模板组件,如果对该类型的操作没有实现 适用要求的语义子条款(...)。

当然,正是因为允许编译器忽略错误并使程序表现良好,与明显预期的行为相匹配的未定义行为。但这不是必须的。

您还应该考虑到您无法预测 Poly 类型的所有未来用途。有人可能会写一些模板函数,例如:

template <class T>
void f(T const& t)
{
    T t2;
    T t3 = t2 = t;
    // ...
}

然后,此函数将不适用于您的 Poly 类。

只要不违反这个 C++ 约定,你就不会遇到麻烦。

【讨论】:

    【解决方案4】:

    当你返回 *this 时会发生什么?

    在您的示例 (p2 = p1;) 中,什么都没有。该方法将p1 复制到p2 并返回对“this”对象的引用,调用代码不使用该对象。

    p3 = p2 = p1; 等代码中,第一个调用是p2 = p1,它将p1 复制到p2 并返回对p2 的引用。然后调用代码从p2 的引用复制到p3(并忽略返回的对p3 的引用)。

    (顺便说一句:您的单元测试是否确保p1 = p1 正常工作?很容易忘记这种情况!)

    【讨论】:

    • 如果你正确地实现了操作符,使用了复制和交换的习语,那么你根本不需要处理自赋值。事实上,if (this != &amp;other) 通常是危险代码的标志。罕见的优化问题可能会例外。
    【解决方案5】:

    返回对目标对象的引用允许赋值链式(级联),类内的运算符重载遵循右关联(运算符重载规则点击here

    Poly a, b, c;
    a = b = c;
    

    【讨论】:

      猜你喜欢
      • 2011-05-22
      • 2011-08-03
      • 2016-09-30
      • 2023-01-06
      • 2017-07-08
      • 2013-11-14
      • 1970-01-01
      • 2013-09-01
      • 1970-01-01
      相关资源
      最近更新 更多