Ответ
Деструкторы автоматических (стековых) объектов гарантированно вызываются при выходе из их области видимости в процессе раскрутки стека (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).
Ответ 18+ 🔞
А, слушай, тут про деструкторы и исключения — тема, конечно, весёлая. Ну, в общем, все знают, что когда объект на стеке выходит из области видимости, его деструктор вызовется. Это святое, это основа RAII, ёпта. Но жизнь, как всегда, оказывается пиздопроебибна, и есть моменты, когда эта железобетонная гарантия накрывается медным тазом. Прям волнение ебать, когда об этом думаешь.
Вот смотри, первый номер программы — исключение, которое улетело из main() и никто его не поймал. Представь: ты в main() создал какую-нибудь ResourceHandle, выбросил исключение, а ловить его некому. Программа говорит: «Всё, пиздец», вызывает 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()
}
Третий пункт — это для любителей острых ощущений, которые лезут в низкоуровневое управление выполнением. Функции longjmp() и setjmp() из Си — это, блядь, чистой воды русская рулетка для объектов C++. Если прыгнешь через кадры стека, где живут твои объекты, получишь неопределённое поведение, а деструкторы — хуй с горы. Ну и вызов std::exit() или std::abort() — это просто приказ на расстрел без права на последнее слово. Раскрутки стека не будет, никаких деструкторов.
И, наконец, четвёртый акт этой драмы — утечка памяти из-за исключения. Классика жанра, ебать копать. Создал объект через new, потом вызвал функцию, которая может выбросить, и delete не сработает. Объект в куче так и останется висеть, как грустный хуй в пальто.
void risky() {
MyClass* ptr = new MyClass;
someFunctionThatMayThrow(); // Может выбросить исключение
delete ptr; // Эта строка может не выполниться
}
Но решение-то, блядь, простое и элегантное, как удар вилкой в глаз. Умные указатели, std::unique_ptr или std::shared_ptr. Их деструкторы вызовут delete за тебя, даже если всё вокруг летит в тартарары. Это и есть тот самый RAII, который спасает от этих ебанутых сценариев. Просто используй их, и живи спокойно.