我一直在思考如何实现各种异常安全保障,尤其是强保障,即异常发生时数据回滚到原来的状态。
考虑以下精心设计的示例(C++11 代码)。假设有一个简单的数据结构存储一些值
struct Data
{
int value = 321;
};
和一些modify()
对该值进行操作的函数
void modify(Data& data, int newValue, bool throwExc = false)
{
data.value = newValue;
if(throwExc)
{
// some exception occurs, sentry will roll-back stuff
throw std::exception();
}
}
(可以看出这是多么做作)。假设我们想为modify()
. 万一出现异常,Data::value
显然不会回滚到原来的值。人们可以天真地继续try
整个功能,在适当的catch
块中手动设置东西,这非常乏味并且根本无法扩展。
另一种方法是使用一些作用域的RAII
助手——有点像哨兵,它知道在发生错误时临时保存和恢复什么:
struct FakeSentry
{
FakeSentry(Data& data) : data_(data), value_(data_.value)
{
}
~FakeSentry()
{
if(!accepted_)
{
// roll-back if accept() wasn't called
data_.value = value_;
}
}
void accept()
{
accepted_ = true;
}
Data& data_ ;
int value_;
bool accepted_ = false;
};
该应用程序很简单,只需要在成功accept()
的情况下调用modify()
:
void modify(Data& data, int newValue, bool throwExc = false)
{
FakeSentry sentry(data);
data.value = newValue;
if(throwExc)
{
// some exception occurs, sentry will roll-back stuff
throw std::exception();
}
// prevent rollback
sentry.accept();
}
这可以完成工作,但也不能很好地扩展。每个不同的用户定义类型都需要有一个哨兵,了解所述类型的所有内部结构。
我现在的问题是:在尝试实现强异常安全代码时,我想到了哪些其他模式、习语或首选的行动方案?