当我有一个方法调用一组提供强有力保证的方法时,我常常遇到回滚更改的问题,以便也有一个强大的保证方法。我们来举个例子:
// Would like this to offer strong guarantee
void MacroMethod() throw(...)
{
int i = 0;
try
{
for(i = 0; i < 100; ++i)
SetMethod(i); // this might throw
}
catch(const std::exception& _e)
{
// Undo changes that were done
for(int j = i; j >= 0; --j)
UnsetMethod(j); // this might throw
throw;
}
}
// Offers strong guarantee
void SetMethod(int i) throw(...)
{
// Does a change on member i
}
// Offers strong guarantee
void UnsetMethod() throw(...)
{
// Undoes a change on member i
}
显然,UnsetMethod可能会抛出。在这种情况下,我的MacroMathod()仅提供基本保证。然而,我尽我所能提供强有力的保证,但我不能绝对确定我的UnsetMethod()不会抛出。这是我的问题:
谢谢!
答案 0 :(得分:6)
尝试实现此目的的一个好方法是使您的方法在您要修改的对象的副本上工作。完成所有修改后,您可以交换对象(应保证交换不要抛出)。这只有在有效实施复制和交换时才有意义。
此方法的优点是代码中不需要任何try...catch
- 块,也没有清理代码。如果抛出异常,则在堆栈展开期间会丢弃修改后的副本,并且根本不会修改原始版本。
答案 1 :(得分:1)
如果强烈的异常保证对MacroMethod()
很重要,我会重新设计UnsetMethod()
,如果可以的话,不要扔任何东西。当然,如何做到这一点取决于你正在做什么。
您使用UnsetMethod()
在SetMethod()
失败后进行清理。如果UnsetMethod()
无法清理,您可以做些什么?这就是为什么从析构函数中抛出异常是极其危险的原因。
答案 2 :(得分:1)
答案 3 :(得分:0)
从根本上说,在调用throw函数的函数中需要做的是在数据的临时副本上工作,直到所有可能抛出的子函数都完成,然后swap
(不能抛出)临时值到'真实'结构。
一些说明性的psudocode:
void MacroMethod() throw(...)
{
int i = 0;
MyValues working_copy[100] = *this; //obviously this is psudocode as I don't know your real code
for(i = 0; i < 100; ++i)
working_vopy[i].SetMethod(i); // if this throws the exception will propogate out, and no changes will be made
swap( *this, working_copy); // this must not throw
}