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

В чем разница между выбросом исключения и std::abort?

1.7 Middle🔥 81 комментариев
#Исключения и обработка ошибок#Язык C++

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

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

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

Разница между выбросом исключения и std::abort

Быстрый ответ

Исключение — это управляемая ошибка, которую можно перехватить и обработать. std::abort() — это аварийное завершение процесса без возможности восстановления.

ХарактеристикаИсключениеstd::abort()
Управление потокомИщет обработчик в стеке вызововНемедленное завершение
ДеструкторыВызываются (stack unwinding)НЕ вызываются
ПерехватМожно try/catchНельзя перехватить
Возвращаемый код0 (успех)Не определён (зависит от ОС)
ИспользованиеОшибки в логике приложенияКритические, неисправимые ошибки

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

Когда выбросишь исключение, программа:

  1. Остаёт стек вызовов (stack unwinding)
  2. Вызывает деструкторы локальных объектов
  3. Ищет соответствующий catch блок
  4. Если найден — продолжает выполнение
  5. Если не найден — вызывает std::terminate() (по умолчанию который вызывает abort)
void cleanup() {
    std::cout << "Очистка ресурсов\n";
}

void process() {
    std::vector<int> data;
    std::cout << "Начало обработки\n";
    
    throw std::runtime_error("Что-то пошло не так");
    cleanup();  // Не выполнится
}

int main() {
    try {
        process();
    } catch (const std::exception& e) {
        std::cout << "Ошибка: " << e.what() << "\n";
        std::cout << "Программа продолжает работу\n";
    }
    return 0;
}

// Вывод:
// Начало обработки
// Ошибка: Что-то пошло не так
// Программа продолжает работу

Stack unwinding и деструкторы

Это самое важное отличие. При исключении вызываются деструкторы всех локальных объектов:

class File {
public:
    File(const std::string& name) { 
        std::cout << "Открыт файл: " << name << "\n"; 
    }
    ~File() { 
        std::cout << "Файл закрыт (деструктор)\n"; 
    }
};

void processFile() {
    File f("data.txt");
    std::cout << "Обработка...\n";
    throw std::runtime_error("Ошибка обработки");
    // f выходит из scope, вызывается ~File()
}

int main() {
    try {
        processFile();
    } catch (const std::exception& e) {
        std::cout << "Обработано: " << e.what() << "\n";
    }
}

// Вывод:
// Открыт файл: data.txt
// Обработка...
// Файл закрыт (деструктор)
// Обработано: Ошибка обработки

std::abort(): безусловное завершение

std::abort() немедленно завершает процесс:

  • Не вызывает деструкторы
  • Не закрывает файлы
  • Не фиксирует состояние БД
  • Не выполняет очистку
void criticalError() {
    std::cout << "Критическая ошибка, завершение\n";
    std::abort();  // Процесс завершится СЕЙЧАС
}

void processFile() {
    File f("data.txt");
    
    if (someCondition) {
        criticalError();  // деструктор f НЕ будет вызван
    }
}

int main() {
    try {
        processFile();
    } catch (...) {
        // abort() нельзя перехватить, catch не сработает
    }
}

// Вывод:
// Открыт файл: data.txt
// Критическая ошибка, завершение
// Файл НЕ закрыт (нет деструктора)
// Процесс завершен со статусом SIGABRT

Когда использовать исключения?

Исключения — для ожидаемых ошибок, которые можно обработать:

// Открытие файла может не удаться
void loadData(const std::string& filename) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        throw std::runtime_error("Не могу открыть файл");
    }
    // Обработка
}

int main() {
    try {
        loadData("missing.txt");
    } catch (const std::runtime_error& e) {
        std::cout << "Используем значение по умолчанию\n";
        return 1;  // Можем продолжить или завершить gracefully
    }
}

Когда использовать abort()?

std::abort() — для неисправимых ошибок, когда продолжение опасно:

// Дефект памяти, инвариант класса нарушен
class Database {
fragile:
    int refCount;
    
public:
    ~Database() {
        if (refCount != 0) {
            std::cerr << "PANIC: refCount не нулевой при удалении\n";
            std::abort();  // Неисправимое состояние
        }
    }
};

// Системный сбой, из которого невозможно восстановиться
if (pthread_mutex_lock(&mutex) != 0) {
    std::cerr << "FATAL: не могу захватить мьютекс\n";
    std::abort();
}

std::terminate() и noexcept

Есть промежуточный вариант — std::terminate():

// Функция не должна выбрасывать исключения
void criticalOperation() noexcept {
    try {
        dangerousCall();
    } catch (...) {
        // Если здесь выброса исключение, вызовется std::terminate
        std::terminate();  // Явный вызов
    }
}

// Другой пример: деструктор, выбросивший исключение
~MyClass() noexcept {
    try {
        cleanup();
    } catch (...) {
        // Исключение в деструкторе = std::terminate()
    }
}

std::terminate() — это обычно abort(), но можно переопределить:

void myTerminateHandler() {
    std::cerr << "Критическая ошибка, логирование...\n";
    // Логирование перед выходом
    std::abort();
}

int main() {
    std::set_terminate(myTerminateHandler);
    // Теперь при uncaught exception вызовется myTerminateHandler
}

Практические примеры

1. Ожидаемая ошибка — исключение:

double divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("Деление на ноль");
    }
    return a / b;
}

2. Неисправимая ошибка — abort:

void* allocateMemory(size_t size) {
    void* ptr = malloc(size);
    if (ptr == nullptr && size > 0) {
        std::cerr << "FATAL: не могу выделить память\n";
        std::abort();  // Нет смысла продолжать
    }
    return ptr;
}

3. Проверка инвариантов — assert:

// Использовать assert для проверки внутренних состояний
class Stack {
public:
    void pop() {
        assert(!empty());  // Вызовет abort в debug режиме
        // ...
    }
};

Заключение

  • Исключение — graceful обработка ошибок с вызовом деструкторов
  • std::abort() — аварийное завершение без очистки
  • Используй исключения для обработки ошибок в коде
  • Используй abort только для неисправимых проблем
  • Используй assert для проверки инвариантов в debug
  • Правильная обработка исключений делает код надёжнее