Исключения — механизм через раскрутку стека
Исключения в C++ — это не «как в Java/Python с другим синтаксисом». Это механизм, неотделимый от раскрутки стека и RAII: язык гарантирует освобождение ресурсов через деструкторы локалов на пути от throw до catch. Без RAII раскрутка работает «через объекты», но сами ресурсы текут. С RAII — наоборот, всё освобождается само.
C++ оставляет много свободы: можно отключить исключения целиком (-fno-exceptions), можно выразить «ожидаемые» ошибки через std::optional/std::expected, можно выбирать гарантии безопасности (nothrow / strong / basic). Ловушки тоже принципиальные: бросок в деструкторе или noexcept-функции — мгновенный std::terminate; move без noexcept отключает оптимизации в std::vector; throw e; вместо throw; теряет тип.
Карта темы
- try/catch — базовый синтаксис — как пишутся
try,catch,throw, какие ошибки нельзя глотать. - Throw by value, catch by reference — каноничное правило C++: бросать по значению, ловить по
const&. - Иерархия std::exception — стандартные классы и собственные исключения через наследование.
- Каскад catch — порядок обработчиков,
catch(...)и иерархическая ловля. - Раскрутка стека — что делают исключения «снизу вверх», что прерывает раскрутку.
- RAII — захват ресурса в конструкторе — почему исключения без RAII опасны; «нет голого владения».
- noexcept — контракт «не бросает» — функциональный атрибут, не косметика; влияет на
std::vector. - Гарантии безопасности — уровни Саттера (nothrow/strong/basic/none), copy-and-swap.
- Деструктор и исключения — двойной бросок →
std::terminate; как обрабатывать опасные операции. - Исключения в конструкторах — деструктор не вызовется; глобалы и
std::terminate. - Повторный бросок и std::exception_ptr —
throw;без операнда, передача между потоками. - Runtime-ошибки — что именно бросает стандартная библиотека и что — нет.
- Альтернативы исключениям —
optional,expected,error_code,-fno-exceptions. - Стратегия обработки ошибок — где исключения, где value-based, как декларировать контракт.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Ловля по значению (catch (std::exception e)) | Срезка — производный тип потерян, виртуальные методы не работают |
Пустой catch(...) {} без throw; | Ошибка молча проглочена, потеряна навсегда |
throw e; вместо throw; | Копия как базовый тип — теряется производный класс |
Базовый тип в catch выше конкретного | Конкретный обработчик — недостижимый код |
| Бросок в деструкторе во время раскрутки | Немедленный std::terminate |
Бросок из noexcept-функции | Немедленный std::terminate, без раскрутки |
Move-конструктор без noexcept | std::vector копирует вместо move при realloc |
Сырой new в init-list или теле конструктора | Утечка при исключении в последующей строке |
| Бросок в конструкторе глобала / static | std::terminate — нет окружающего try |
| Исключения как control flow на hot path | На порядок дороже условных ветвей |
try/catch вокруг noexcept операций | Мёртвый код; маскирует баги |
Значение для собеседований
Исключения — тема, где интервьюер быстро отличает поверхностное знание от глубокого. Синтаксис try/catch знают все; настоящие вопросы начинаются после.
Что реально проверяется:
- Связка раскрутки стека и RAII — почему одно без другого ломается.
- Гарантии безопасности (nothrow/strong/basic) и как реализуется strong через copy-and-swap.
- Почему
noexceptфункционален:std::vectorrealloc выбирает move только приnoexcept. - Что вызывает
std::terminate— три-четыре сценария наизусть. - Чем
throw;отличается отthrow e;и почему первое сохраняет тип. - Когда стоит использовать
std::optional/std::expectedвместо исключений.
Популярные вопросы:
- Объясните stack unwinding. Что происходит с локальными объектами?
- Какие гарантии безопасности исключений вы знаете? Реализуйте strong для
operator=. - Почему move-конструктор должен быть
noexcept? - Что вызывает
std::terminate()? Назовите минимум три случая. - Как передать исключение из дочернего потока в основной?
- Чем
throw;отличается отthrow e;? - Когда исключения — неподходящий инструмент?
Частая ошибка кандидата: считать исключения «как в Java/Python, только с другим синтаксисом». В C++ они неотделимы от RAII и раскрутки стека — без понимания этой связи ответы будут поверхностными.