Почему деструктор не вызывается по исключению? - PullRequest
49 голосов
/ 21 октября 2008

Я ожидал, что в этой программе будет вызван 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() вызывается .

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

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

Ответы [ 6 ]

65 голосов
/ 21 октября 2008

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

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

В соответствии с черновым стандартом , п. 15.3, пул 9:

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.
14 голосов
/ 22 октября 2008

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

2 голосов
/ 11 мая 2016

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

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

2 голосов
/ 21 октября 2008

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

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

  • Механизм обработки исключений не может найти обработчик для брошенного исключения.
    Ниже приведены более конкретные случаи этого:
    • Во время разматывания стека исключение исключается из деструктора.
    • Брошенное выражение, исключение исключается из конструктора.
    • Исключение ускользает от конструктора / деструктора нелокальной статики (т. Е. Глобальной)
    • Исключение исключается из функции, зарегистрированной с помощью atexit ().
    • Исключение исключается из main ()
  • Попытка перебросить исключение, если в данный момент не распространяется ни одно исключение.
  • Неожиданное исключение ускользает от функции со спецификаторами исключений (через неожиданное)
2 голосов
/ 21 октября 2008

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

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

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

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

1 голос
/ 21 октября 2008

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

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

...