Почему деструктор не вызывается при исключении?

Я ожидал, что в этой программе будет вызван A::~A(), но это не так:

#include <iostream>

struct A {
  ~A() { std::cout << "~A()" << std::endl; }
};

void f() {
  A a;
  throw "spam";
}

int main() { f(); }

Однако, если я изменю последнюю строку на

int main() try { f(); } catch (...) { throw; }

затем вызывается A::~A() .

Я компилирую с помощью «32-разрядного оптимизирующего компилятора Microsoft (R) C / C ++ версии 14.00.50727.762 для 80x86» из Visual Studio 2005. Командная строка - cl /EHa my.cpp.

Компилятор как обычно прав? Что стандарт говорит по этому поводу?


person Constantin    schedule 21.10.2008    source источник
comment
Для информации я воспроизвел ту же проблему с тем же кодом в Visual C ++ 2003. +1 за вопрос.   -  person paercebal    schedule 21.10.2008


Ответы (6)


Деструктор не вызывается, потому что terminate () для необработанного исключения вызывается до того, как стек будет развернут.

Конкретные детали того, что говорится в спецификации C ++, мне неизвестны, но трассировка отладки с помощью gdb и g ++, похоже, подтверждает это.

Согласно пункту 9 пункта 15.3 раздела 15.3 проекта стандарта черновик стандарта:

9 If no matching handler is found in a program, the function terminate()
  (_except.terminate_)  is  called.  Whether or not the stack is unwound
  before calling terminate() is implementation-defined.
person lefticus    schedule 21.10.2008
comment
Проголосуйте за ссылку на официальный стандартный документ. - person comfreak; 02.04.2019

Спецификация языка C ++ гласит: Процесс вызова деструкторов для автоматических объектов, созданных на пути от блока try к выражению throw, называется «раскручиванием стека». Ваш исходный код не содержит блока try, который Вот почему разматывания стека не происходит.

person Alex Che    schedule 22.10.2008
comment
В выбранном ответе упоминается terminate функция, которая является основной причиной. Однако я думаю, что этот ответ ближе к правильному. Если не использовать try, кажется, что раскрутки стека не произойдет, и поэтому деструктор не будет вызван. Мой эксперимент, казалось, предполагал, что деструктор вызывается при обнаружении правильной ошибки до того, как будет запущен блок кода, следующий за catch. - person Zheng Liu; 30.06.2019

Извините, у меня нет при себе копии стандарта.
Я определенно хотел бы получить окончательный ответ на этот вопрос, поэтому кто-то, у кого есть копия стандарта, хочет поделиться главой и стихом о том, что происходит:

Насколько я понимаю, terminate вызывается только тогда, когда:

  • The exception handling mechanism cannot find a handler for a thrown exception.
    The following are more specific cases of this:
    • During stack unwinding, an exception escapes a destructor.
    • Выброшенное выражение, исключение ускользает от конструктора.
    • Исключение ускользает от конструктора / деструктора нелокального статического (т. Е. Глобального)
    • Исключение ускользает от функции, зарегистрированной с помощью atexit ().
    • Исключение ускользает от main ()
  • Попытка повторно вызвать исключение, если в данный момент исключение не распространяется.
  • Неожиданное исключение ускользает от функции со спецификаторами исключения (через неожиданное)
person Martin York    schedule 21.10.2008

Во втором примере dtor вызывается, когда он выходит из блока try {}.

В первом примере dtor вызывается, когда программа завершает работу после выхода из функции main () - к этому времени cout, возможно, уже был уничтожен.

person James Curran    schedule 21.10.2008
comment
Нет, это неправильно. Деструктор гарантированно будет вызван до того, как программа покинет main. Деструктор cout гарантированно будет вызван впоследствии. - person Konrad Rudolph; 21.10.2008
comment
… Исправление: деструктор a должен быть вызван до того, как f останется! - person Konrad Rudolph; 21.10.2008
comment
Кроме того, в первом примере программа никогда не покидает main. Он прерывается через завершение с помощью неожиданного исключения. - person ejgottl; 21.10.2008
comment
@ejgottl: в этом примере не выполняется ни одно из условий для завершения через terminate () или неожиданное (). неожиданному () потребуется спецификация throw, а terminate () потребуется исключение в dtor. - person James Curran; 21.10.2008
comment
@ejgotti: независимо от того, даже если был вызван terminate (), при выключении cout БУДЕТ ПЕРЕСТАНОВИТЬ работу, что было моей точкой зрения. - person James Curran; 21.10.2008
comment
@James: деструктор для всех объектов, локальных для f (), должен быть вызван до того, как он уйдет, независимо от того, как он уйдет (возврат / исключение). В противном случае RIAA не будет работать. Все еще выясняя, почему вызывается termiate (), он никогда не возвращается, поэтому нет разматывания стека. - person Martin York; 21.10.2008

Я также предположил, что компилятор не генерирует код относительно «a», поскольку он не упоминается, но все же это неправильное поведение, поскольку деструктор делает что-то, что должно быть выполнено.

Итак, я пробовал в VS2008 / vc9 (+ SP1), Debug and Release, и ~ A вызывается после того, как исключение выбрасывается, выходя из f () - это правильное поведение, если я прав.

Теперь я просто попробовал с VS2005 / vc8 (+ SP1), и это то же самое поведение.

Для уверенности я использовал точки останова. Я только что проверил консоль, и у меня тоже есть сообщение «~ A». Может ты где-то еще ошибся?

person Klaim    schedule 21.10.2008
comment
Я создал текстовый файл с первым примером (без попытки), открыл командную строку Visual Studio 2005 и скомпилировал файл с cl /EHa my.cpp. Результат выполнения: это приложение запросило среду выполнения необычным способом. Пожалуйста, свяжитесь со службой поддержки приложения для получения дополнительной информации. - person Constantin; 21.10.2008
comment
Я не знаком с параметрами компиляции в командной строке, но я предполагаю, что это похоже на режим Release, где, если нет try / catch, код не уйдет дальше, чем инструкция throw (что происходит, когда я пытаюсь) и приложение просто выйдет из строя (это желаемое поведение) - person Klaim; 21.10.2008
comment
Посмотрев на листинги сборок, я считаю, что оптимизация по умолчанию отключена. Так что это ближе к отладке. Кстати, см. Комментарий paercebal, ему / ей удалось воспроизвести с VS2003. - person Constantin; 21.10.2008

Этот вопрос легко задать в Google, поэтому я поделюсь своей ситуацией здесь.

Убедитесь, что ваше исключение не пересекает extern "C" границу, или используйте параметр MSVC / EHs (Включить исключения C ++ = Да с функциями Extern C (/ EHs))

person light_keeper    schedule 11.05.2016