Ответ
В C++ деструктор по умолчанию неявно помечен как noexcept (начиная с C++11). Если деструктор, вызванный в процессе раскрутки стека (stack unwinding) из-за другого активного исключения, попытается выбросить своё исключение, программа немедленно завершится через вызов std::terminate().
Почему это опасно: Во время раскрутки стека уничтожаются объекты в области видимости для очистки ресурсов. Если на этом этапе возникнет новое исключение, система исключений C++ окажется в неконсистентном состоянии (какое из двух исключений обрабатывать?), что приводит к аварийному завершению.
Пример проблемного кода:
#include <iostream>
#include <stdexcept>
struct ResourceHolder {
~ResourceHolder() noexcept(false) { // Явно разрешаем исключения – опасно!
// ... какая-то очистка, которая может завершиться ошибкой
throw std::runtime_error("Cleanup failed in destructor!");
}
};
int main() {
try {
ResourceHolder rh;
throw std::logic_error("Something went wrong in main logic"); // Первое исключение
// Деструктор `rh` будет вызван при раскрутке стека.
// Если он выбросит своё исключение -> вызовется std::terminate()
} catch (const std::exception& e) {
// Сюда мы, скорее всего, не попадём
std::cout << "Caught: " << e.what() << std::endl;
}
}
// Программа завершится через std::terminate()
Правильный подход:
- Не бросать исключения в деструкторах. Это главное правило.
- Логировать ошибки. Если операция очистки может закончиться неудачей (например, закрытие файла или сетевого соединения), лучше записать ошибку в лог, но не прерывать выполнение.
-
Предоставлять отдельный метод
close()илиrelease(). Для ресурсов, требующих явного освобождения с проверкой ошибок, вынесите эту логику в отдельный метод, который можно вызвать до разрушения объекта.struct SafeFile { FILE* handle = nullptr; bool close() { // Явный метод для обработки ошибок if (handle && fclose(handle) != 0) { // Логируем ошибку, но не бросаем исключение perror("fclose failed"); return false; } handle = nullptr; return true; } ~SafeFile() noexcept { // Деструктор теперь безопасен if (handle) { // Игнорируем ошибку, так как в деструкторе мы бессильны fclose(handle); } } };