在C ++中复制构造函数和=运算符重载:是一个常用函数吗?
由于一个拷贝构造函数
MyClass(const MyClass&);
和an =运算符过载
MyClass& operator = (const MyClass&);
具有几乎相同的代码,相同的参数,并且仅在返回时有所不同,是否可以为它们使用共同的函数?
是。 有两个常用选项。 其中之一 - 我不建议 - 从显式拷贝构造函数中调用operator=
:
MyClass(const MyClass& other)
{
operator=(other);
}
然而,提供一个好的operator=
对于处理旧的状态和自我分配产生的问题来说是一个挑战。 此外,所有成员和基地都会默认初始化,即使将它们分配给other
基地也是如此。 这对于所有成员和基地来说甚至都不是有效的,甚至在它有效的情况下,它在语义上是多余的,并且实际上可能很昂贵。
越来越流行的解决方案是实现operator=
使用拷贝构造函数和交换方法。
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
甚至:
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
swap
函数通常很容易编写,因为它只是交换内部的所有权,无需清理现有状态或分配新资源。
复制和交换习惯用法的优点是它自动地自行分配安全,并且 - 如果交换操作是非抛出 - 也强烈地是异常安全的。
为了强有力的异常安全,一个“手写”赋值运算符通常必须在取消分配受让人的旧资源之前分配新资源的副本,以便如果发生异常时分配新资源,旧状态仍可返回。 所有这些都是通过复制和交换免费提供的,但通常更复杂,因此容易出错,无法从头开始。
要注意的一件事是确保swap方法是真正的交换,而不是使用拷贝构造函数和赋值运算符本身的默认std::swap
。
通常使用成员swap
。 std::swap
可以工作,并且在所有基本类型和指针类型的保证下都是“无丢包”。 大多数智能指针也可以通过无丢包保证进行交换。
复制构造函数执行用于初始内存的对象的首次初始化。 赋值运算符OTOH用新值覆盖现有值。 通常情况下,这会涉及废弃旧资源(例如内存)和分配新资源。
如果两者之间存在相似性,那么赋值运算符会执行销毁和复制构建。 一些开发人员过去实际上是通过就地销毁来实施分配,然后是放置复制构建。 但是,这是一个非常糟糕的主意。 (如果这是在派生类的赋值期间调用的基类的赋值运算符,该怎么办?)
现在通常认为典型的习惯用法是使用swap
正如查尔斯所说的那样:
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
这使用了复制构造(注意other
被复制)和销毁(它在函数的末尾被销毁) - 并且它也按照正确的顺序使用它们:销毁前的构造(可能失败)(不能失败)。
有些事情困扰我:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
首先,当我的思想“复制”时,阅读“交换”一词会激怒我的常识。 另外,我质疑这个花哨戏法的目标。 是的,构建新(复制)资源时的任何异常都应该在交换之前发生,这似乎是一种安全的方式,以确保所有新数据在投入使用前都已被填充。
没关系。 那么,交换之后发生的异常呢? (当临时对象超出范围时破坏旧资源时)从作业用户的角度来看,操作失败,除非没有。 它有一个巨大的副作用:副本确实发生。 这只是一些资源清理失败。 即使外部操作看起来失败,目标对象的状态也会改变。
所以,我建议不要用“交换”来做更自然的“转移”:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
临时对象的构造仍然存在,但下一步的即时操作是在移动源(并使其为空值,以便它们不会被双重释放)之前释放目标的所有当前资源。
我建议{构建,破坏,移动}而不是{构建,移动,破坏}。 此举是最危险的行动,是在所有其他事情得到解决之后最后一次采取的行动。
是的,销毁失败在这两种方案中都是一个问题。 数据被破坏(当你不认为是时候被复制)或者丢失(当你不认为它是被释放时)。 丢失比腐败更好。 没有数据比坏数据好。
转移而不是交换。 无论如何,这是我的建议。
链接地址: http://www.djcxy.com/p/12669.html上一篇: Copy constructor and = operator overload in C++: is a common function possible?