Деструктор может выполнять различные действия в зависимости от того, произошло ли исключение
У меня есть код для обновления таблицы базы данных, которая выглядит как
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
Вы можете использовать следующую логику:
- Добавьте логическое значение commit_done, инициализированное как false, к вашему классу транзакций.
- В вашем конструкторе "начните" транзакцию.
- Добавьте метод для "фиксации" транзакции и соответственно обновите commit_done.
- В вашем деструкторе вызывайте "откат", только если 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"
}
Различия с вашим исходным кодом мотивировали мой ответ:
В "catch" ничего не нужно: деструктор будет предполагать автоматический откат, если только метод commit() не был вызван с успехом (который мог бы, например, установить для некоторого частного логического члена DBTransaction значение true). Уловка - то, где код продолжится, предполагая, что транзакция не удалась
Вы должны создать специальное исключение (я назвал его DBTransactionRollback), чтобы выдавать момент, когда что-то не работает в одной из ваших команд. Таким образом, catch будет перехватывать только исключение, вызванное откатом транзакции, а не другие исключения (например, STL и т. Д.).
Использование механизма исключений позволяет вам помещать код в несколько функций, вызываемых из этого блока кода try / catch, без необходимости обрабатывать логические и другие возвраты кода ошибки.
Надеюсь, что это ответ на ваш вопрос.
Самым простым способом, который я могу придумать, было бы установить в классе частную переменную-член в исключении и протестировать ее / выполнить соответствующее действие в деструкторе.