Как обнаружить разматывание стека в деструкторе
У меня есть простой объект C++, который я создаю в начале функции F(), чтобы гарантировать, что две совпадающие функции (OpDo, OpUndo) вызываются в начале и возвращении F(), используя конструктор объекта и деструктор. Однако я не хочу, чтобы операция была отменена, если в теле F () было сгенерировано исключение. Можно ли это сделать чисто? Я читал о std:: uncaught-exception, но его использование не рекомендуется.
3 ответа
Большинство людей использовали std::uncaught_exception()
попытаться определить, находится ли исключение в ожидании, чтобы они могли выбросить исключение из деструктора, если его еще нет. Это обычно считается не хорошей идеей.
Если вы не хотите отменять операцию, если было сгенерировано исключение, она должна сделать свое дело.
Помните, что деструктор - это ваш последний шанс высвободить любые ресурсы, которые есть у объекта, потому что после того, как деструктор закончил, объект не существует, и любые ресурсы, которые он удерживал, теперь утекли навсегда. Если OpDo()
выделяет любую память или дескрипторы файлов или что-то еще, вам нужно иметь дело с этим в деструкторе, несмотря ни на что.
Вы можете подорвать идиому Scope Guard. Вместо того чтобы ничего не делать в деструкторе, когда не генерируется исключение, мы инвертируем это и делаем что-то, только если не сгенерировано исключение
class DoUndoRAII{
public:
DoUndoRAII()
: noexcept_(false)
{
// your stuff here
}
~DoUndoRAII(){
if(noexcept_){
// do whatever you need to do
}
}
void no_exception(){
noexcept_ = true;
}
private:
bool noexcept_;
};
void func(){
DoUndoRAII do_undo;
// last line
do_undo.no_exception();
}
Когда выдается исключение, do_undo.no_exception()
никогда не будет вызван и, следовательно, никогда не установить noexcept_
значение к истине.:) Пример можно найти здесь на Ideone.
Давайте предположим, что ваш F возвращает некоторый класс Helper:
Helper F()
{
MyClass doUndoWrapper;
}
Когда поток нормальный - хелпер создан. При возникновении исключения копия Helper не создается. Попробуйте использовать эту семантику, поместив в приватный регион конструктор Helper и объявив F другом, чтобы никто не мог создать помощника.
class Helper
{
private:
friend Helper F();
Helper(){ //place there OpDo semantic - first entry
// construct this class
Helper(const Helper& copy){ //this must present to allow stack operations
// copy constructor will be called at end of `F` to return value
// so place OpUndo semantic there to mark success without exception