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

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

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 ]

12 голосов
/ 09 апреля 2010

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

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

3 голосов
/ 09 апреля 2010

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

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

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

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

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

1 голос
/ 09 апреля 2010

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

...