← Назад к вопросам

Можно ли выбросить исключение из деструктора?

1.0 Junior🔥 121 комментариев
#Исключения и обработка ошибок

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Можно ли выбросить исключение из деструктора?

Краткий ответ: НЕТ, технически можно, но это крайне опасно и считается плохой практикой. Выброс исключения из деструктора может привести к преждевременному завершению программы.

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

Деструкторы вызываются в двух ситуациях:

  1. Обычное завершение объекта — когда объект выходит из области видимости
  2. Обработка исключения — когда выполняется раскручивание стека из-за выброшенного исключения

Если во время раскручивания стека (когда уже обрабатывается исключение) деструктор выбросит ещё одно исключение, программа немедленно вызовет std::terminate() и завершится.

Пример опасного кода

class Resource {
public:
    ~Resource() {
        // ОПАСНО! Может выбросить исключение
        if (file.is_open()) {
            file.close();  // может выбросить если ошибка записи
            throw std::runtime_error("Ошибка закрытия");
        }
    }
private:
    std::ofstream file;
};

int main() {
    try {
        Resource r;
        throw std::logic_error("Что-то пошло не так");
        // ~Resource вызовется во время раскручивания
        // Если он выбросит исключение — программа упадёт!
    } catch (const std::exception& e) {
        std::cerr << e.what() << '\n';
    }
}

Правильный способ: RAII с гарантией noexcept

class Resource {
public:
    ~Resource() noexcept {
        // Гарантия: исключений не будет
        try {
            if (file.is_open()) {
                file.close();
            }
        } catch (const std::exception& e) {
            // Логируем ошибку, но не выбрасываем
            std::cerr << "Ошибка при закрытии файла: " << e.what() << '\n';
        }
    }
private:
    std::ofstream file;
};

Лучшая практика: явное управление ошибками

class Resource {
public:
    // Явный метод закрытия с обработкой ошибок
    void close() {
        if (file.is_open()) {
            if (!file.close().fail()) {
                throw std::runtime_error("Ошибка закрытия файла");
            }
        }
    }
    
    ~Resource() noexcept {
        // Деструктор не выбрасывает исключений
        try {
            close();
        } catch (...) {
            // Подавляем исключение
        }
    }
private:
    std::ofstream file;
};

Когда фактически выброс из деструктора вызовет std::terminate

std::vector<Resource> resources;

try {
    resources.emplace_back();  // Создаём Resource
    throw std::logic_error("Ошибка");  // Выбрасываем
    // Во время раскручивания вызовется ~Resource
    // Если он выбросит исключение → std::terminate()
} catch (...) {}

Спецификация исключений (C++11 и старше)

// C++11 и новее: используй noexcept вместо throw()
class GoodResource {
public:
    ~GoodResource() noexcept {  // ✅ Гарантия: не выбрасывает
        // ...
    }
};

// Старый стиль (C++98/03) — не использовать
class OldResource {
public:
    ~OldResource() throw() {  // ❌ Устарело
        // ...
    }
};

Правило нулевой стоимости (Zero-overhead)

Исключения в деструкторах нарушают принцип RAII — ты не можешь полагаться на то, что деструктор завершится успешно. Правильно спроектированные классы:

  • Деструкторы помечены noexcept и не выбрасывают исключений
  • Ошибки обрабатываются явно перед уничтожением объекта
  • Логирование ошибок в деструкторе без выброса исключений

Резюме

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

Можно ли выбросить исключение из деструктора? | PrepBro