3 回答
TA贡献1839条经验 获得超15个赞
复制构造函数对曾经是原始内存的对象执行首次初始化。赋值运算符OTOH用新值覆盖现有值。通常涉及解散旧资源(例如,内存)并分配新资源。
如果两者之间有相似之处,那就是赋值运算符执行销毁和复制构造。一些开发人员过去实际上是通过就地销毁,然后是布局复制构造来实现分配的。但是,这是一个非常糟糕的主意。(如果这是派生类分配期间调用的基类的赋值运算符,该怎么办?)
swap正如Charles所建议的,如今通常被认为是规范用语的是:
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
这使用了复制构造(注意other已复制)和销毁(它在函数末尾被销毁)-并且它也以正确的顺序使用它们:销毁(必须失败)之前的构造(可能失败)。
TA贡献1812条经验 获得超5个赞
有些事情困扰着我:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
首先,当我的思想是“复制”时,读“交换”一词会激怒我的常识。另外,我对这个幻想的目的提出了质疑。是的,在构造新的(复制的)资源时,任何异常都应该发生在交换之前,这似乎是一种确保所有新数据都被填充后才能上线的安全方法。
没关系。那么,交换之后发生的异常又如何呢?(当临时对象超出范围时,旧资源被破坏时)从分配用户的角度来看,该操作失败了,但没有失败。它具有巨大的副作用:复制确实发生了。只是某些资源清除失败。即使从外部看来操作失败,目标对象的状态也已更改。
因此,我建议不要“交换”来进行更自然的“转移”:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
仍然存在临时对象的构造,但是下一个直接的操作是在将源的资源移动到目的地之前,释放目标的所有当前资源(并且为NULL,这样它们就不会被双重释放)。
我提出了{构造,破坏,移动}而不是{构造,移动,破坏}。此举是最危险的举动,是在解决所有其他问题后采取的最后一步。
是的,无论哪种方案,销毁失败都是一个问题。数据已损坏(在您不认为是复制时复制)或丢失(在您不认为是被释放时释放)。丢失总比损坏好。没有数据比坏数据更好。
转移而不是交换。无论如何,这是我的建议。
- 3 回答
- 0 关注
- 428 浏览
添加回答
举报