强大的异常安全保证表明,如果发生异常,操作不会更改任何程序状态。实现异常安全副本分配的一种优雅方式是copy-and-swap idiom。
我的问题是:
对于改变非原始类型的类的每个变异操作,使用copy-and-swap是否过度?
对于强烈的异常安全,性能真的是公平的交易吗?
例如:
class A
{
public:
void increment()
{
// Copy
A tmp(*this);
// Perform throwing operations on the copy
++(tmp.x);
tmp.x.crazyStuff();
// Now that the operation is done sans exceptions,
// change program state
swap(tmp);
}
int setSomeProperty(int q)
{
A tmp(*this);
tmp.y.setProperty("q", q);
int rc = tmp.x.otherCrazyStuff();
swap(tmp);
return rc;
}
//
// And many others similarly
//
void swap(const A &a)
{
// Non-throwing swap
}
private:
SomeClass x;
OtherClass y;
};
答案 0 :(得分:3)
您应始终瞄准基本异常保证:确保在发生异常时,所有资源都已正确释放且对象处于有效状态(可以是未定义但有效)。
强烈的异常保证(即“交易”)是您认为有意义时应该实现的:您并不总是需要交易行为。
如果很容易实现交易操作(例如通过复制和交换),那么就这样做。但有时它不是,或者它会产生很大的性能影响,即使对于像赋值算子这样的基本事物也是如此。我记得像boost :: variant这样的东西,我不能总是在复制赋值中提供强有力的保证。
你遇到的一个巨大困难是移动语义。移动时做想要交易,否则会丢失移动的对象。但是,您无法始终提供强有力的保证:考虑std::pair<movable_nothrow, copyable>
(并查看评论)。这是你必须成为noexcept
艺术家的地方,并使用一个令人不舒服的元编程。由于异常安全,C ++很难精确地 。
答案 1 :(得分:2)
所有工程问题都与平衡有关。
当然,const
- ness / immutability和强大的保证增加了对一个人代码的信心(特别是伴随着测试)。它们还有助于减少可能的错误解释空间。
但是,它们可能会对性能产生影响。
就像所有表现问题一样,我会说:个人资料并摆脱热点。复制和交换肯定是不是实现事务语义的唯一方法(它只是最简单的),因此分析会告诉你应该绝对不使用它,你必须找到替代方案。 / p>
答案 2 :(得分:1)
这取决于您的应用程序将在何种环境中运行。如果您只是在自己的计算机上运行它(频谱的一端),则可能不值得对异常安全性过于严格。如果你正在写一个程序,例如对于医疗设备(另一端),您不希望在发生异常时留下无意的副作用。中间的任何内容取决于对错误的容忍程度和可用于开发的资源(时间,金钱等)
答案 3 :(得分:0)
是的,你面临的问题是这个成语很难扩展。其他答案都没有提到它,但Alexandrescu发明的另一个非常有趣的成语是scopeGuards。它有助于提高代码的经济性,并在需要符合强大异常安全保证的函数的可读性方面做出巨大改进。
范围保护的想法是一个堆栈实例,它只允许将回滚功能对象附加到每个资源请求。当范围保护被破坏(通过例外)时,将调用回滚。您需要在正常流中显式调用commit()
以避免在作用域出口处进行回滚调用。
使用c ++ 11功能检查与设计安全范围保护相关的this recent question from me。