2010-10-25 2 views
6

У меня есть простой объект C++, который я создаю в начале функции F() для обеспечения вызова двух согласованных функций (OpDo, OpUndo) в начале и в конце F() , используя конструктор объекта и деструктор. Тем не менее, я не хочу, чтобы операция была отменена, если исключение было выбрано внутри тела F(). Можно ли это сделать чисто? Я прочитал около std :: uncaught-exception, но его использование, похоже, не рекомендуется.Как обнаружить разматывание стека в деструкторе

+0

Использование'uncaught_exception 'может быть в порядке, но это зависит от того, что вы делаете (и, следовательно, субъективно ..). Тем не менее, не могли бы вы подробнее рассказать о операции do/undo и об исключении (-ях)? – Macke

+3

Если вы не хотите, чтобы какое-либо действие произошло, когда исключение распространялось, тогда просто не используйте RAII. Просто поставьте OpDo() и OpUndo() прямо в код. –

+1

«его использование, по-видимому, не рекомендуется», поскольку оно не работает как средство обнаружения того, что бит стека, содержащий ваш объект, разматывается из-за исключения или из-за нормального возврата. Предположим, что кто-то помещает некоторый код в деструктор, который вызывает вашу функцию F. Предположим далее, что такой объект уничтожается как часть раскрутки стека во время исключения. Тогда 'uncaught_exception' вернет' true', но исключение не было выбрано внутри тела F(). –

ответ

6

Большинство людей использовали std::uncaught_exception(), чтобы попытаться рассказать, ожидает ли исключение, поэтому они могут генерировать исключение из деструктора, если его еще нет. Это обычно считается не хорошей идеей.

Если вы хотите не отменить операцию, если возникло исключение, она должна сделать трюк.

Помните, что деструктор - это ваш последний шанс освободить любые ресурсы, которые имеет объект, потому что после завершения деструктора объект не существует и все оставшиеся ресурсы теперь просочились. Если OpDo() выделяет любые файлы памяти или файлов или что-то еще, вам нужно иметь дело с этим в деструкторе, независимо от того, что.

0

Давайте предположим, что ваш F возвращает некоторый класс Helper:

Helper F() 
{ 
    MyClass doUndoWrapper; 
} 

Когда поток нормально - Helper создается. Когда возникает исключение, копия Helper не создается. Попробуйте использовать эту семантику, поместив ее в частный конструктор области Helper и объявив F как друга - так что никто не мог создать помощника.

+0

Что? Это не совсем четкий ответ. –

+0

@C Johnson - Если вы используете «Помощник» в качестве возвращаемого значения, вы получаете следующие преимущества (1) Помощник создается в начале по мере необходимости (поэтому поместите OpDo в конструктор) (2) Вы можете узнать, успешно ли функция F вернулась - вызванный конструктор копирования (так что место OpUndo там) (3) в случае исключения не вызвал конструктор копирования. Чтобы прояснить это, я меняю код, чтобы отразить эту идею. – Dewfy

0

Вы можете подобрать Scope Guard idiom. Вместо того, чтобы не делать что-то в деструкторе, если исключение не брошено, мы инвертировать, что и только сделать что-то, если исключение не брошено:

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_ в значение true. :) Пример можно найти here on Ideone.

Смежные вопросы