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





Во втором примере dtor вызывается, когда он выходит из блока try {}.
В первом примере dtor вызывается, когда программа завершает работу после выхода из функции main () - к этому времени cout, возможно, уже был уничтожен.
Нет, это не так. Деструктор гарантированно будет вызван до того, как программа покинет main. Деструктор cout гарантированно называется после.
… Исправление: деструктор a должен быть вызван до выхода f!
Кроме того, в первом примере программа никогда не покидает main. Он прерывается с помощью terminate с помощью «неожиданного исключения».
@ejgottl: в этом примере не выполняется ни одно из условий для завершения через terminate () или unknown (). неожиданному () потребуется спецификация throw, а terminate () потребуется исключение в dtor.
@ejgotti: независимо от того, даже если был вызван terminate (), при выключении cout БУДЕТ ПЕРЕСТАНОВИТЬ работу, что было моей точкой зрения.
@James: деструктор для всех объектов, локальных для f (), должен быть вызван до того, как он уйдет, независимо от того, как он уйдет (возврат / исключение). В противном случае RIAA не сработает. Все еще выясняя, почему вызывается termiate (), он никогда не возвращается, поэтому нет разматывания стека.
Я также предположил, что компилятор не генерирует код относительно «a», поскольку на него нет ссылок, но, тем не менее, это неправильное поведение, поскольку деструктор делает что-то, что должно быть выполнено.
Итак, я пробовал в VS2008 / vc9 (+ SP1), Debug and Release, и ~ A вызывается после того, как исключение выбрасывается, выходя из f () - это правильное поведение, если я прав.
Теперь я просто попробовал VS2005 / vc8 (+ SP1), и это то же самое поведение.
Для уверенности я использовал точки останова. Я только что проверил консоль, и у меня тоже есть сообщение «~ A». Может ты где-то еще ошибся?
Я создал текстовый файл с первым примером (без попытки), открыл «Командную строку Visual Studio 2005» и скомпилировал файл с помощью cl /EHa my.cpp. Результат выполнения: это приложение запросило среду выполнения необычным способом. Пожалуйста, свяжитесь со службой поддержки приложения для получения дополнительной информации.
Я не знаком с параметрами компиляции в командной строке, но я предполагаю, что это похоже на режим Release, где, если нет try / catch, код не уйдет дальше, чем инструкция throw (что происходит, когда я пытаюсь) и приложение просто "рухнет" (это желаемое поведение)
Посмотрев листинги сборок, я считаю, что оптимизация по умолчанию отключена. Так что это ближе к отладке. Кстати, см. Комментарий paercebal, ему / ей удалось воспроизвести с VS2003.
Деструктор не вызывается, потому что 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.
Проголосуйте за ссылку на официальный стандартный документ.
Извините, у меня нет копии стандарта на себе.
Я определенно хотел бы получить окончательный ответ на этот вопрос, поэтому кто-нибудь, у кого есть копия стандарта, захочет поделиться главой и стихом о том, что происходит:
Насколько я понимаю, terminate вызывается только тогда, когда:
Спецификация языка C++ гласит: Процесс вызова деструкторов для автоматических объектов, созданных на пути от блока try к выражению throw, называется «раскручиванием стека». Ваш исходный код не содержит блока try, поэтому раскручивания стека не происходит.
В выбранном ответе упоминается функция terminate, которая является основной причиной. Однако я думаю, что этот ответ ближе к правильному. Если не использовать try, кажется, что раскрутки стека не произойдет, и поэтому деструктор не будет вызван. Мой эксперимент, казалось, предполагал, что деструктор вызывается при обнаружении правильной ошибки до того, как будет запущен блок кода, следующий за catch.
Этот вопрос легко задать в Google, поэтому я поделюсь своей ситуацией здесь.
Убедитесь, что ваше исключение не пересекает границу extern "C", или используйте параметр MSVC / EHs (Включить исключения C++ = Да с функциями Extern C (/ EHs))
Для информации я воспроизвел ту же проблему с тем же кодом в Visual C++ 2003. +1 за вопрос.