由于复制构造函数
MyClass(const MyClass&);
和an =运算符重载
MyClass& operator = (const MyClass&);
有几乎相同的代码,相同的参数,并且只在返回上不同,有可能有一个共同的函数供它们使用吗?
发布于 2009-11-14 23:57:56
是。有两个常见的选项。一种是从复制构造函数显式调用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
函数通常很容易编写,因为它只交换内部的所有权,而不必清理现有状态或分配新资源。
复制和交换习惯用法的优点是它是自动自赋值安全的,并且-如果交换操作是不抛出的-也是强异常安全的。
为了强异常安全,“手”写的赋值操作符通常必须在解除分配被分配人的旧资源之前分配新资源的副本,以便如果分配新资源时发生异常,旧状态仍然可以返回。所有这些都是通过复制和交换免费提供的,但是从零开始通常更复杂,因此容易出错。
要注意的一件事是确保交换方法是真正的交换,而不是使用复制构造函数和赋值操作符本身的默认std::swap
。
通常使用成员级swap
。std::swap
可以工作,并且保证所有基本类型和指针类型都不会抛出。大多数智能指针也可以在无抛出保证的情况下交换。
发布于 2010-01-18 13:35:11
有件事让我很困扰:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
首先,当我的大脑在思考“复制”时,读到“交换”这个词会刺激我的常识。另外,我质疑这个花哨的把戏的目的。是的,构造新的(复制的)资源时的任何异常都应该在交换之前发生,这似乎是一种安全的方法,可以确保所有新数据在上线之前都已被填充。
这很好。那么,交换之后会发生什么异常呢?(当临时对象超出作用域时,旧资源被销毁)从分配的用户的角度来看,操作已经失败,除非它没有。它有一个巨大的副作用:复制确实发生了。只是一些资源清理失败了。目标对象的状态已更改,即使从外部看操作似乎已失败。
因此,我建议用一个更自然的“转移”来代替“交换”:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
仍然需要构造临时对象,但是下一个直接的动作是在将源的资源转移到它之前释放目标的所有当前资源(以及NULLing,这样它们就不会被双重释放)。
不是{构造,移动,析构},我建议{构造,析构,移动}。这一行动是最危险的行动,是在其他一切都解决之后最后采取的行动。
是的,破坏失败在这两种方案中都是一个问题。数据要么被破坏(当你认为它不是被复制的时候被复制),要么被丢失(当你不认为它是被释放的时候被释放)。失落总比败坏好。没有数据比坏数据更好。
传输而不是交换。不管怎样,这是我的建议。
https://stackoverflow.com/questions/1734628
复制相似问题