Наличие деструктора принимает различные меры в зависимости от того, произошло ли исключение

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

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
}

но как я записал бы деструктор для него?

6
задан dan04 9 April 2010 в 07:02
поделиться

4 ответа

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

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

Когда tr.commit () завершается, он устанавливает состояние "фиксация выполнена", а деструктор ничего не делает, {{1} } иначе происходит откат.

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

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

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

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

12
ответ дан 8 December 2019 в 12:58
поделиться

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

1
ответ дан 8 December 2019 в 12:58
поделиться

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

  1. Добавьте логическое значение commit_done , инициализированное значением false , в свой класс транзакций.
  2. В конструкторе «начните» транзакцию.
  3. Добавьте метод "фиксации" транзакции и соответствующим образом обновите commit_done .
  4. В деструкторе вызывайте «откат», только если commit_done по-прежнему false
3
ответ дан 8 December 2019 в 12:58
поделиться

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

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

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

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

2
ответ дан 8 December 2019 в 12:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: