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

Ответ

throw (выброс исключения) — это механизм контролируемой обработки ошибок. Он передает управление по стеку вызовов до первого подходящего блока catch. При этом корректно вызываются деструкторы всех локальных объектов в раскручиваемых фреймах стека (это называется stack unwinding и является основой идиомы RAII).

std::abort() — это функция из <cstdlib>, которая вызывает немедленное аварийное завершение программы (SIGABRT в POSIX). Деструкторы не вызываются, блоки catch не рассматриваются. Это жесткое завершение без какой-либо очистки.

Сравнение на практике:

class FileHandler {
    FILE* f;
public:
    FileHandler(const char* name) : f(fopen(name, "r")) {
        if (!f) throw std::runtime_error("Cannot open file"); // Восстановимая ошибка
    }
    ~FileHandler() { if (f) fclose(f); std::cout << "File closed.n"; }
    void read() { /* ... */ }
};

void riskyOperation() {
    FileHandler fh("data.txt"); // RAII-объект
    // ... какая-то работа ...
    if (criticalCondition) {
        // НЕПРАВИЛЬНО: вызовет утечку ресурса (файл не закроется).
        // std::abort();
        // ПРАВИЛЬНО: исключение вызовет деструктор fh.
        throw std::logic_error("Invalid state detected");
    }
}

int main() {
    try {
        riskyOperation();
    } catch (const std::exception& e) {
        std::cerr << "Ошибка перехвачена: " << e.what() << std::endl;
        // Программа продолжает работу, можно попробовать восстановиться.
        return 1;
    }
    return 0;
}

Когда что использовать:

  • Исключения (throw/catch) — для обработки ожидаемых, потенциально восстановимых ошибок (ошибки ввода/вывода, некорректные данные, сетевая недоступность). Это основной механизм обработки ошибок в современном C++.
  • std::abort() — только для фатальных, невосстановимых ошибок, когда продолжение работы программы бессмысленно или опасно (например, обнаружено критическое повреждение внутренних структур данных, нарушение инвариантов, которые не должны происходить никогда — assert по умолчанию вызывает abort).

Ответ 18+ 🔞

Э, слушай, история про исключения и аборт — это как сравнивать вежливое "извините, я сейчас упаду" с резким "всё, пиздец, приехали". Давай разберём, пока не поздно.

Вот смотри, throw — это как цивилизованный способ накосячить. Ты такой: "ой, всё, я облажался", и программа начинает разматывать стек, как клубок ниток. Главная фишка в том, что все локальные объекты по пути аккуратно уничтожаются, вызываются их деструкторы. Это и есть та самая магия RAII: файлы закроются, память освободится, мьютексы откроются — красота. Управление летит вверх по вызовам, пока не наткнётся на подходящий catch, который может сказать "ничего страшного, бывает" и попробовать всё починить.

А теперь std::abort() — это полная противоположность. Это как взять и выдернуть шнур из розетки. Ёпта, программа просто падает на месте. Никаких деструкторов, никакой очистки, никаких catch. Просто мгновенная смерть с вызовом SIGABRT. Ресурсы могут повиснуть, логи не дописаться — полный пиздец, одним словом.

Смотри, как это на практике выглядит:

class FileHandler {
    FILE* f;
public:
    FileHandler(const char* name) : f(fopen(name, "r")) {
        if (!f) throw std::runtime_error("Cannot open file"); // Ну не открылся, бывает
    }
    ~FileHandler() { if (f) fclose(f); std::cout << "File closed.n"; }
    void read() { /* ... */ }
};

void riskyOperation() {
    FileHandler fh("data.txt"); // Объект, который сам за собой уберёт
    // ... работаем ...
    if (criticalCondition) {
        // Если тут вызвать std::abort() — файл так и останется висеть открытым, утечка.
        // А вот throw — вызовет деструктор fh, файл закроется, и только потом полетит ошибка.
        throw std::logic_error("Invalid state detected");
    }
}

int main() {
    try {
        riskyOperation();
    } catch (const std::exception& e) {
        std::cerr << "Ошибка перехвачена: " << e.what() << std::endl;
        // Пережили, живём дальше, можно что-то сделать.
        return 1;
    }
    return 0;
}

Так когда что юзать, чтобы не выглядеть как полный распиздяй?

  • throw/catch — это твой основной инструмент для нормальных, ожидаемых косяков. Файл не найден? Сеть легла? Пользователь ввёл хуйню вместо числа? Всё это — повод бросить исключение. Программа может перехватить его, извиниться перед юзером и попробовать ещё раз. Доверия к такому коду — выше крыши.
  • std::abort() — это уже ядерный вариант, последнее предупреждение. Только для ситуаций, когда продолжать работу совершенно невозможно и опасно. Сломался аллокатор? Нарушился критический инвариант, который вообще не должен нарушаться? Вот тогда — да, abort(), и пусть всё падает. Часто его вызывает assert() при провале проверки. Это как крик "терпения ноль, ебать!" — дальше только перезапуск.

Короче, исключения — для взрослых, кто умеет убирать за собой. abort() — для паникёров, которые кричат "всё пропало!" и бьют стеклянную дверь. Выбирай с умом, а то будет тебе хиросима, а не обработка ошибок.