Ответ
Деструкторы автоматических (стековых) объектов гарантированно вызываются при выходе из их области видимости в процессе раскрутки стека (stack unwinding). Однако есть сценарии, где эта гарантия нарушается:
-
Исключение, покинувшее функцию
main()без перехвата. Программа завершается вызовомstd::terminate(), и раскрутка стека не гарантируется.int main() { ResourceHandle res; // Деструктор может не вызваться throw std::runtime_error("Uncaught!"); // Завершение через std::terminate() } -
Исключение, выброшенное в деструкторе во время активной обработки другого исключения. Это приводит к немедленному вызову
std::terminate()(правило: деструкторы не должны выбрасывать исключения).struct Bad { ~Bad() noexcept(false) { throw 42; } // Опасность! }; int main() { try { Bad obj; throw std::runtime_error("First"); // Начало раскрутки стека } catch(...) { } // Сюда не попадем, вызовется terminate() } -
Некорректное использование низкоуровневых средств управления потоком выполнения, обходящих механизм исключений C++.
- Функции
longjmp()/setjmp()из языка C. Переходlongjmp()через кадры стека с объектами C++ ведет к неопределенному поведению. - Прямой вызов
std::exit()илиstd::abort(). Раскрутка стека не производится.
- Функции
-
Утечка указателя на динамически выделенную память. Если исключение возникает до вызова
deleteдля сырого указателя, деструктор объекта в куче не вызовется.void risky() { MyClass* ptr = new MyClass; someFunctionThatMayThrow(); // Может выбросить исключение delete ptr; // Эта строка может не выполниться }Решение: Использовать умные указатели (
std::unique_ptr,std::shared_ptr), чьи деструкторы вызовутdeleteдаже при исключении (паттерн RAII).