根据是否发生异常,让析构函数采取不同的操作

时间:2010-04-09 07:02:48

标签: c++ exception transactions raii

我有一些代码来更新看起来像

的数据库表
try
{
   db.execute("BEGIN");
   // Lots of DELETE and INSERT     
   db.execute("COMMIT");
}
catch (DBException&)
{
   db.execute("ROLLBACK");
}

我想将事务逻辑包装在RAII类中,以便我可以编写

{
   DBTransaction trans(db);
   // Lots of DELETE and INSERT
}

但我该如何为它编写析构函数?

4 个答案:

答案 0 :(得分:12)

使用以下内容:

transaction tr(db);
...
tr.commit();

tr.commit()完成时,它将状态设置为“commit done”,析构函数不执行任何操作, 否则它会回滚。

检查异常是个坏主意,请考虑:

transaction tr(db);
...
if(something_wrong)
   return; // Not throw
...
tr.commit();

在这种情况下,您可能希望 rollback 然后提交,但提交将完成。

修改,但如果您仍然想要它,请先查看std::uncaught_exception()先阅读 http://www.gotw.ca/gotw/047.htm

答案 1 :(得分:3)

您可以使用以下逻辑:

  1. 将初始化为 false commit_done 布尔值添加到您的Transaction类。
  2. 在构造函数中,“开始”事务。
  3. 添加“提交”交易的方法并相应地更新 commit_done
  4. 在析构函数中,仅当 commit_done false
  5. 时才调用“rollback”

答案 2 :(得分:2)

通过删除异常处理,您正在削弱RAII。

代码应为

try
{
   DBTransaction trans(db) ;

   // Lots of DELETE and INSERT
   // should one fail, a DBTransactionRollback exception will be thrown

   trans.commit() ;
}
catch(const DBTransactionRollback & e)
{
   // If really needed, you could extract failure information from "e"
}

与原始代码的不同之处在于我的回答:

  1. “catch”中没有任何内容:析构函数将假定自动回滚,除非成功调用了commit()方法(例如,可以设置DBTransaction的一些私有布尔成员真的)。假设交易失败,捕获是代码将继续的位置。

  2. 您应该创建一个专用异常(我将其命名为DBTransactionRollback),以便在您的某个命令中丢失某些内容。因此,catch只会捕获事务回滚驱动的异常,而不是其他异常(如STL等)

  3. 使用异常机制可以将代码放在多个函数中,从这个try / catch代码块调用,而不必处理布尔返回和其他错误代码返回。

    < / LI>

    希望这能回答你的问题。

答案 3 :(得分:1)

我能想到的最简单的方法是在异常中的类中设置一个私有成员变量,并在析构函数中测试它/执行适当的操作。