Вызываются ли деструкторы после броска в C ++?

Я запустил пример программы, и действительно вызываются деструкторы для объектов, выделенных стеком, но гарантируется ли это стандартом?


person Luchian Grigore    schedule 29.11.2011    source источник
comment
Конечно да. От этого зависит RAII, который является одной из самых важных идиом в C ++.   -  person Jon    schedule 29.11.2011
comment
Да, в этом весь смысл обработки исключений.   -  person Kerrek SB    schedule 29.11.2011
comment
@Jon & Kerrek SB, если исключение не обнаружено, раскрутка стека не гарантируется, это определяется реализацией: см. Ответ NPE ниже, последняя часть - это цитата из стандарта, в котором говорится об этом.   -  person Philippe Carphin    schedule 17.09.2020


Ответы (3)


Да, это гарантировано (при условии, что исключение перехвачено), вплоть до порядка, в котором вызываются деструкторы:

C ++ 11 15.2 Конструкторы и деструкторы [except.ctor]

1 Когда управление переходит от выражения throw к обработчику, деструкторы вызываются для всех автоматических объектов, созданных с момента входа в блок try. Автоматические объекты уничтожаются в порядке, обратном завершению их строительства.

Кроме того, если во время создания объекта возникает исключение, подобъекты частично построенного объекта гарантированно будут правильно уничтожены:

2 Объект любой продолжительности хранения, инициализация или уничтожение которого завершается исключением, будет иметь деструкторы, выполняемые для всех его полностью сконструированных подобъектов (за исключением вариантов членов объединенного класса), то есть для подобъектов, для которых главный конструктор (12.6.2) завершил выполнение, а деструктор еще не начал выполнение. Точно так же, если не делегирующий конструктор для объекта завершил выполнение и делегирующий конструктор для этого объекта завершился с исключением, деструктор объекта будет вызван. Если объект был выделен в новом выражении, соответствующая функция освобождения (3.7.4.2, 5.3.4, 12.5), если таковая имеется, вызывается для освобождения памяти, занимаемой объектом.

Весь этот процесс известен как «раскрутка стека»:

3 Процесс вызова деструкторов для автоматических объектов, построенных на пути от блока try к выражению throw, называется «раскручиванием стека». Если деструктор, вызываемый во время раскрутки стека, завершается с исключением, вызывается std :: terminate (15.5.1).

Раскрутка стека составляет основу широко используемой техники, называемой инициализация получения ресурсов (RAII).

Обратите внимание, что раскрутка стека не обязательно выполняется, если исключение не перехвачено. В этом случае выполнение раскрутки стека зависит от реализации. Но независимо от того, выполняется ли раскрутка стека или нет, в этом случае вам гарантирован последний вызов std::terminate.

C ++ 11 15.5.1 Функция std :: terminate () [except.terminate]

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

person NPE    schedule 29.11.2011
comment
Примечание: относительно прерванного строительства объекта. Сам объект не уничтожается (на самом деле он никогда не существовал), гарантируется, что подчасти (базовые классы, атрибуты), которые были полностью построены до сих пор, будут уничтожены в обратном порядке. - person Matthieu M.; 29.11.2011
comment
Добавлена ​​информация о раскручивании стека (или нет) для неперехваченного исключения. - person Cheers and hth. - Alf; 29.11.2011

Да, деструкторы гарантированно будут вызываться при раскручивании стека, в том числе при раскручивании из-за возникшего исключения. Есть лишь несколько приемов, за исключением которых вы должны запомнить:

  • Деструктор класса не вызывается, если в его конструкторе возникло исключение.
  • Исключение автоматически генерируется повторно, если оно попадает в блок catch списка инициализации конструкции.
person Community    schedule 29.11.2011
comment
3) Деструкторы не должны никогда генерировать исключения, поскольку нет способа их адекватно обработать. - person DevSolar; 29.11.2011
comment
@DevSolar существуют контрпримеры. - person Cheers and hth. - Alf; 29.11.2011
comment
@ AlfP.Steinbach: любой деструктор, сгенерированный во время раскрутки стека (из-за другого исключения), terminate() будет вашим процессом. Мне было бы интересно увидеть контрпримеры ... - person DevSolar; 29.11.2011
comment
@DevSolar: вы (намеренно?) Не знаете, к чему бы вы хотели привести контрпримеры. но что касается первого утверждения, что деструкторы никогда не должны генерировать исключения, не совсем необычным контрпримером является объект, который представляет результат функции и который выдает из своего деструктора, если вызывающий код не проверил, представляет ли он сбой. Другой пример - объект защиты транзакции, который генерируется из своего деструктора, если только код, в который он встроен, не преуспел в своих усилиях (например, передать право собственности на что-либо) и вызвал свой метод release. - person Cheers and hth. - Alf; 29.11.2011
comment
проблема в том, что, как вы заметили, выброс из деструктора может быть довольно фатальным, если есть необработанное исключение (а не раскрутка стека, поскольку вы можете выполнить try в деструкторе). Visual C ++ никогда не реализовывал стандартную функцию для проверки, и эта функция в любом случае неадекватна. так что это немного проблематично, но не полная демонстрация, поскольку код использования может быть разработан вокруг этого. - person Cheers and hth. - Alf; 29.11.2011
comment
@ AlfP.Steinbach: Код использования может быть разработан вокруг этого. - Я согласен. Однако эмпирическое правило заключается в том, что ваши объекты могут использоваться в контексте, который вы не предвидели, и не могут использоваться проактивно. И если вы поместите объекты, вызывающие деструктор, в вектор STL, например, и этот вектор будет уничтожен из-за некоторого несвязанного разматывания стека исключений, он вызовет деструктор вашего объекта, и ваше приложение перестанет работать. Конечно, я могу вытащить штифт из гранаты, немного осторожно с ним обращаться и вставить штифт обратно. Но я никогда не должен этого делать ... - person DevSolar; 29.11.2011

Если перехватывается throw, обычно операции cpp продолжаются. Это включает деструкторы и выталкивание стека. Однако, если исключение не обнаружено, выталкивание стека не гарантируется.

Также мой мобильный компилятор не может поймать пустой бросок или пустой бросок.

пример:

#include <Jav/report.h>

int main()
{
 try { throw; }
 catch(...) { rep("I bet this is not caught"); }
 }
person Community    schedule 19.06.2019
comment
Я поставил этому плюс один. Он не только не перехватывается, но и не вызываются деструкторы для автоматических объектов в блоке try (достаточно легко вставить один) (g ++ 7.4.0 / clang ++ 6.0.0 ubuntu), -std = c ++ [11 | 14 | 17 ]. Кажется, не помогает использовать noexcept с основным объявлением. Я также попробовал соответствующие параметры страницы руководства. Я могу получить предупреждение за простой бросок в блоке catch, но не в блоке try. Если я что-то упускаю, просветите пожалуйста. - person davernator; 26.07.2019
comment
@davernator Спасибо за ваш плюс один. Надеюсь, вы ничего не пропустили, потому что это выше моей зарплаты. Даже сейчас. Мир снаружи. - person ; 13.03.2020