我正在查看第五版C ++ Primer中的代码示例。在页512,他们提供了operator=
的示例代码,如下所示:
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
他们正确地争辩说,如果你按照这个顺序做事情,即使是自我分配也能正常工作。但是,我一直看到检查明确完成的建议:
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
if (&rhs == this) return *this; // early exit if self assignment
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
这可以避免额外的内存分配/释放步骤。
任何人都可以解释为什么他们强调编写处理自我分配的代码而不是仅仅提前退出自我分配?
答案 0 :(得分:5)
我一直看到检查明确完成的建议
你一直在寻找错误的地方,例如Sutter& Sons的 C ++编码标准 Alexandrescu的。
在大多数程序中,自我分配极为罕见,因此即使检查几乎总是假的,显式检查也会为每个自我分配增加一小笔费用。如果你的作业操作符写得正确,即使是在自我分配的情况下,那么在绝大多数情况下你都不会得到支票的费用,如果发生自我指派,它仍然有效。
Sutter& Alexandrescu显示了一个用swap
成员编写的赋值运算符:
Foo& operator=(const Foo& other)
{
Foo(other).swap(*this);
return *this;
}
这样可以安全地防止自我赋值,异常安全,并重复使用复制构造函数,因此您不必显式实现赋值。
答案 1 :(得分:2)
重要的是代码在自我分配(或自我复制)下正常工作,包括异常安全。鉴于此,实施是否包括明确的自我测试的问题主要是性能问题。但是,一种或另一种选择是否更好,取决于使用。例如。如果自我操作很少发生或从不发生,测试本身会使代码变慢。
因此,在没有实际性能测量的情况下使用自检可能是过早优化的一个例子。我看到了相反的建议,例如在Stroustrup的书中,除了测量表明它们是有意义的,否则它会避免自我测试。
答案 2 :(得分:0)
如果我们放弃这本书的作者只想扩展读者的大脑的假设,我们可以假设编写操作员建议的方式的赋值操作符可以用来削减内部动态缓冲区(或者做一些其他有用的东西)。例如,使用std::vector
,您需要vector(me).swap(me);
来修剪内存,而使用花哨的赋值运算符,您只需说me=me;
。