我搜索了如何在整个互联网上正确实施+运营商,我发现的所有结果都执行以下步骤:
const MyClass MyClass::operator+(const MyClass &other) const
{
MyClass result = *this; // Make a copy of myself. Same as MyClass result(*this);
result += other; // Use += to add other to the copy.
return result; // All done!
}
关于这个“过程”我几乎没有问题:
用这种方式实现+运算符不是很愚蠢,它在第一行调用赋值运算符(复制类),然后在返回中调用复制构造函数(也复制类,因为回报是按价值计算的,所以它会破坏第一份副本并创建一个新版本......坦白说这并不是很聪明......)
当我写a = b + c时,b + c部分创建该类的新副本,然后'a ='部分将副本复制给自己。 谁删除了b + c创建的副本?
有没有更好的方法来实现+运算符而不需要两次处理类,也没有任何内存问题?
提前致谢
答案 0 :(得分:6)
这实际上不是赋值运算符,而是复制构造函数。毕竟像添加这样的操作会创建一个新值,因此必须在某处创建它。这比看起来更有效,因为编译器可以自由地进行返回值优化,这意味着它可以直接在下一次使用它的位置构造值。
result
被声明为局部变量,因此在函数调用中消失 - 除非使用了RVO(见上文),在这种情况下它从未在函数中实际创建,但是在来电者中。
不是真的;这种方法比起初看起来效率更高。
答案 1 :(得分:5)
在这种情况下,我可能会考虑这样的事情:
MyClass MyClass::operator+(MyClass other) {
other += *this;
return other;
}
Dave Abrahams写了article一段时间回来解释这是如何工作的,以及为什么这种代码通常非常有效,即使它最初似乎不应该是。
编辑(谢谢MSalters):是的,这确实假设/依赖于MyClass
的交换属性。如果是a+b != b+a
,则原始代码就是您想要的(大多数相同的推理都适用)。
答案 2 :(得分:3)
这似乎是实施operator+
的正确方法。几点:
MyClass result = *this
不使用赋值运算符,它应该调用复制构造函数,就好像它是MyClass result(*this)
一样。a = b + c
中使用时返回的值称为临时,编译器负责删除它(这可能发生在语句的末尾,即分号,其他一切都完成之后)。您不必担心,编译器将始终清理临时工。答案 3 :(得分:3)
它在第一行调用赋值运算符(复制类)
不,这是复制初始化(通过构造函数)。
然后返回复制构造函数(也复制类
编译器可以(并且通常会)使用NRVO来删除此副本。
当我写a = b + c时,b + c部分创建该类的新副本,然后'a ='部分将副本复制给自己。谁删除了b + c创建的副本
编译器,与任何其他临时值一样。它们在完整表达结束时被删除(在这种情况下,它表示在行尾;
处或之后。
有没有更好的方法来实现+运算符而不需要两次处理类,也没有任何内存问题?
不是真的。这不是那么低效。
答案 4 :(得分:2)
我会尽力回答:
Point(1):不,它不会调用赋值运算符。相反,它调用一个构造函数。由于您无论如何都需要构造对象(因为operator+
返回一个副本),这不会引入额外的操作。
Point(2):临时result
是在堆栈中创建的,因此不会引入内存问题(当函数退出时会被销毁)。在return
上创建临时值,以便即使在a
被销毁后,也可以使用赋值(或复制构造函数)将结果分配给a=b+c;
(result
) 。该临时文件由编译器自动销毁。
第(3)点:以上是标准规定的内容。请记住,只要效果与标准规定的相同,就允许编译器实现者优化实现。我相信,编译器在现实中优化了许多在这里发生的复制。使用上面的习语是可读的,实际上效率不高。
P.S。我有时更喜欢将operator+
实现为非成员,以便为运营商的双方利用隐式转换(只有在有意义的情况下)。
答案 5 :(得分:1)
没有内存问题(假设赋值运算符和复制构造函数写得很好)。只是因为这些对象的所有内存都在堆栈中并由编译器管理。此外,编译器会对最终a
进行优化并直接执行所有操作,而不是复制两次。
答案 6 :(得分:1)
这是在C ++中实现operator +的正确方法。您非常害怕的大多数副本都会被编译器剔除,并且会受到C ++ 0x中移动语义的影响。
该课程是临时课程,将被删除。如果将临时值绑定到const&
,则临时的生命周期将延长到const引用的生命周期。
将其作为免费功能实现可能会更加明显。 MyClass :: operator +中的第一个参数是隐式的,编译器会将函数重写为operator +(const MyClass&,const MyClass&)。
答案 7 :(得分:1)
据我记忆,Stroustrup的'C ++编程语言'建议仅在内部表示受操作影响时才将操作符实现为成员函数,而不是当外部函数影响外部函数时。如果基于operator + =实现,operator +不需要访问内部表示。
所以你会:
class MyClass
{
public:
MyClass& operator+=(const MyClass &other)
{
// Implementation
return *this;
}
};
MyClass operator+(const MyClass &op1, const MyClass &op2)
{
MyClass r = op1;
return r += op2;
}