Деструктор может выполнять различные действия в зависимости от того, произошло ли исключение

У меня есть код для обновления таблицы базы данных, которая выглядит как

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 ответа

Решение

Используйте следующее:

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

когда tr.commit() завершает, устанавливает состояние "совершено", а деструктор ничего не делает, в противном случае выполняется откат.

Проверка на исключение - плохая идея, подумайте:

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

В этом случае вы, вероятно, ожидаете скорее отката, чем коммита, но коммит будет выполнен.

Изменить: но если вы все еще хотите это сильно, посмотрите на std::uncaught_exception() но сначала прочтите это http://www.gotw.ca/gotw/047.htm

Вы можете использовать следующую логику:

  1. Добавьте логическое значение commit_done, инициализированное как false, к вашему классу транзакций.
  2. В вашем конструкторе "начните" транзакцию.
  3. Добавьте метод для "фиксации" транзакции и соответственно обновите commit_done.
  4. В вашем деструкторе вызывайте "откат", только если commit_done все еще равен false

Удаляя обработку исключений, вы наносите вред своему 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 значение true). Уловка - то, где код продолжится, предполагая, что транзакция не удалась

  2. Вы должны создать специальное исключение (я назвал его DBTransactionRollback), чтобы выдавать момент, когда что-то не работает в одной из ваших команд. Таким образом, catch будет перехватывать только исключение, вызванное откатом транзакции, а не другие исключения (например, STL и т. Д.).

  3. Использование механизма исключений позволяет вам помещать код в несколько функций, вызываемых из этого блока кода try / catch, без необходимости обрабатывать логические и другие возвраты кода ошибки.

Надеюсь, что это ответ на ваш вопрос.

Самым простым способом, который я могу придумать, было бы установить в классе частную переменную-член в исключении и протестировать ее / выполнить соответствующее действие в деструкторе.

Другие вопросы по тегам