在C++中重载+和+=运算符

3

我正在进行个人练习,并希望确保我的理解是正确的。我有一个包含行和列成员的坐标类。我想要重载+和+=运算符。以下是我的代码:

Coordinate& Coordinate :: operator+= (const Coordinate& rhs){
    this->m_Row += rhs.m_Row;
    this->m_Column += rhs.m_Column;

    return *this;
}

Coordinate& operator+ (const Coordinate& lhs, const Coordinate& rhs) {
    return Coordinate(lhs) += rhs;
}

在哪里

friend Coordinate& operator + (const Coordinate& lhs, const Coordinate& rhs);

这是在Coordinate类中定义的友元函数。

这段代码有什么注意事项吗?

以下是我对它们工作原理的理解:

operator += 

rhs 的 m_Row 和 m_Column 添加到 this 成员中。返回一个指向此指针指向的对象的引用,从而避免由于复制构造函数而创建另一个对象。
operator +

创建一个名为localObj的本地对象,使用复制构造函数从lhs中创建它(因为lhs是一个常量,我们不想修改它的内容)。在localObj上调用+=成员运算符进行加法操作。返回对此localObj的引用,以避免由于复制构造函数而创建另一个对象。

现在,最后一条语句让我担心,因为我正在返回对本地对象的引用。当函数(operator+)超出范围时,localObj将被销毁,返回的引用将指向已被销毁的对象。我的理解正确吗?

如果是这样,我该如何修复它?

编辑: 在所有答案和我学到的东西之后:现在我的Coordinate类看起来像这样:http://rextester.com/MJJI7394


你的operator +返回了本地对象的引用。 - Mr.Anubis
@Mr.Anubis 是的,这与我给出的解释一致吗?(请参见粗体文本) - brainydexter
抱歉,我没有完全阅读问题。 - Mr.Anubis
@Mr.Anubis 不用担心,我想确认一下我的理解。 - brainydexter
1
没有必要将operator+声明为友元,因为它只访问Coordinate的公共接口。 - celtschk
显示剩余2条评论
2个回答

6

您的担心是正确的,这里返回了一个临时引用:

Coordinate& operator+ (const Coordinate& lhs, const Coordinate& rhs) {
    return Coordinate(lhs) += rhs;
}

你需要返回一个值为Coordinate的坐标,例如像这样:
Coordinate operator+ (Coordinate lhs, const Coordinate& rhs) {
    return lhs += rhs;
}

在上面的示例中,我们复制了第一个参数而不是在函数体中引用它然后再复制。然后,我们通过值返回+=的结果。
有了这个设置,就不需要声明operator+为友元。
有关更多信息,请参见此SO链接,并感谢@Blastfurnace指出它。

1
@Gir 这是一个临时的变量,因为它是函数体内部返回引用的 Coordinate 对象。该对象在退出函数范围时被销毁,因此调用方的引用将会指向无效的地址。 - juanchopanza
为什么它在函数体内?它通过引用接收了lhs(即b)。然后+=运算符添加了rhs(即c),并返回对lhs的引用。我没有看到任何在函数内分配的变量会随着函数的结束而消失。啊,没事了。现在注意到了复制构造函数。 - Gir
1
@Gir 这个 Coordinate(lhs) += rhs 只存在于函数的作用域中,但是它的引用被返回了。这是不好的。 - juanchopanza
2
@brainydexter 是的,也不是。编译器允许进行拷贝省略,特别是返回值优化。为什么不通过引用传递lhs?因为我们不能修改它,而且最终可能还是会进行一次拷贝,所以通过值传递可以让编译器执行更多的拷贝省略,而不是通过引用传递。 - juanchopanza
1
@brainydexter:在这个运算符重载问题中有很好的指南。用operator+=定义operator+是一种推荐的做法。 - Blastfurnace
显示剩余7条评论

0

就我个人而言,我会将operator+=()的定义建立在operator+()operator=()之上:

Coordinate operator+(const Coordinate& lhs, const Coordinate& rhs) {
  return Coordinate(lhs.getRow() + rhs.getRow(), lhs.getCol() + rhs.getCol();
}

const Coordinate& operator=(Coordinate& lhs, const Coordinate& rhs) {
  lhs.setRow(rhs.getRow());
  lhs.setCol(rhs.setCol());

  return lhs;
}

const Coordinate& operator+=(Coordinate& lhs, const Coordinate&rhs) {
  return lhs = lhs + rhs;
}

我在这里使用setter和getter。或者,您可以使用friend和/或member函数。请注意,所有返回引用的函数都会返回发送的参数,以便不会出现对局部或临时对象的引用问题。


1
个人而言,为什么要这样做呢?现在你的 operator+= 会进行不必要的拷贝以及不必要的赋值操作。 - Benjamin Lindley
这段代码无法编译。你试图更改一个__const引用__的lhs - Blastfurnace
我不同意这个观点(尽管我在很多地方都看到过这样做)。这是一个好的链接:https://dev59.com/62855IYBdhLWcg3wUCWC。 - juanchopanza
@BenjaminLindley 或许这不是最高效的方法,但在我看来更易读、更清晰。 - Code-Apprentice
4
对于谁来说更易读?标准的C++习惯是通过+=来定义+;甚至可以通过从一个模板实例派生来实现这一点。偏离标准习惯会使代码更难阅读,因为不太符合预期。(不管个人对标准习惯的看法如何,有些我也不是很满意。) - James Kanze
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接