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

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

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

Для информации я воспроизвел ту же проблему с тем же кодом в Visual C++ 2003. +1 за вопрос.

paercebal 21.10.2008 19:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
55
1
12 684
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

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

Нет, это не так. Деструктор гарантированно будет вызван до того, как программа покинет main. Деструктор cout гарантированно называется после.

Konrad Rudolph 21.10.2008 19:05

… Исправление: деструктор a должен быть вызван до выхода f!

Konrad Rudolph 21.10.2008 19:07

Кроме того, в первом примере программа никогда не покидает main. Он прерывается с помощью terminate с помощью «неожиданного исключения».

ejgottl 21.10.2008 19:08

@ejgottl: в этом примере не выполняется ни одно из условий для завершения через terminate () или unknown (). неожиданному () потребуется спецификация throw, а terminate () потребуется исключение в dtor.

James Curran 21.10.2008 19:16

@ejgotti: независимо от того, даже если был вызван terminate (), при выключении cout БУДЕТ ПЕРЕСТАНОВИТЬ работу, что было моей точкой зрения.

James Curran 21.10.2008 19:17

@James: деструктор для всех объектов, локальных для f (), должен быть вызван до того, как он уйдет, независимо от того, как он уйдет (возврат / исключение). В противном случае RIAA не сработает. Все еще выясняя, почему вызывается termiate (), он никогда не возвращается, поэтому нет разматывания стека.

Martin York 21.10.2008 19:20

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

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

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

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

Я создал текстовый файл с первым примером (без попытки), открыл «Командную строку Visual Studio 2005» и скомпилировал файл с помощью cl /EHa my.cpp. Результат выполнения: это приложение запросило среду выполнения необычным способом. Пожалуйста, свяжитесь со службой поддержки приложения для получения дополнительной информации.

Constantin 21.10.2008 19:16

Я не знаком с параметрами компиляции в командной строке, но я предполагаю, что это похоже на режим Release, где, если нет try / catch, код не уйдет дальше, чем инструкция throw (что происходит, когда я пытаюсь) и приложение просто "рухнет" (это желаемое поведение)

Klaim 21.10.2008 19:44

Посмотрев листинги сборок, я считаю, что оптимизация по умолчанию отключена. Так что это ближе к отладке. Кстати, см. Комментарий paercebal, ему / ей удалось воспроизвести с VS2003.

Constantin 21.10.2008 20:11
Ответ принят как подходящий

Деструктор не вызывается, потому что 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.

Проголосуйте за ссылку на официальный стандартный документ.

comfreak 02.04.2019 20:22

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

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

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

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

В выбранном ответе упоминается функция terminate, которая является основной причиной. Однако я думаю, что этот ответ ближе к правильному. Если не использовать try, кажется, что раскрутки стека не произойдет, и поэтому деструктор не будет вызван. Мой эксперимент, казалось, предполагал, что деструктор вызывается при обнаружении правильной ошибки до того, как будет запущен блок кода, следующий за catch.

Zheng Liu 30.06.2019 07:04

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

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

Другие вопросы по теме