ПрограммированиеC++ системный программист

Расскажите о механизмах работы исключений (exceptions) в C++. Как обрабатывать ошибки корректно и зачем нужен noexcept?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса:

Механизм обработки исключений был введён в C++ для обеспечения более надёжной и структурированной обработки ошибок, в отличие от кодов возврата. С годами синтаксис расширился, появились спецификаторы типа noexcept для контроля отмены выбрасывания исключений из функций.

Проблема:

Неправильная работа с исключениями часто приводит к утечкам памяти, неопределённому поведению или падению приложений. Если не учесть бросание исключений из конструкторов, деструкторов или при работе с ресурсами, возникает серьёзная проблема с состоянием программы.

Решение:

Базовый синтаксис — использование try-catch. Исключения могут быть любого типа, но рекомендуется наследовать пользовательские от std::exception. Ключевое слово noexcept (C++11+) указывает, что функция не должна выбрасывать исключения; бросок исключения из noexcept-функции вызывает std::terminate().

Пример кода:

#include <iostream> #include <stdexcept> void func() noexcept(false) { throw std::runtime_error("Error!"); } int main() { try { func(); } catch(const std::exception& ex) { std::cout << ex.what(); } }

Ключевые особенности:

  • Исключения обеспечивают автоматический unwind (разворачивание стека)
  • noexcept позволяет компилятору оптимизировать, если функция гарантированно не бросает
  • Только std::exception и её производные гарантируют наличие метода what()

Вопросы с подвохом.

Допустимо ли бросать исключения в деструкторе?

Нет, бросок исключения из деструктора во время стекового развёртывания приводит к std::terminate(). Исключения в деструкторе надо перехватывать внутри самого деструктора.

Что произойдёт, если выбросить исключение из noexcept-функции?

Вызовется std::terminate, программа аварийно завершится. noexcept — строгий контракт.

Можно ли ловить исключения по value? Чем это опасно?

Ловить по value можно, но объект копируется (object slicing). Рекомендуется использовать const &.

Пример кода:

try { throw std::out_of_range("err"); } catch (const std::exception& e) { /* ... */ }

Типовые ошибки и анти-паттерны

  • Использование throw в деструкторах
  • Проглатывание исключений catch(...) без логирования
  • Неуказание noexcept там, где это возможно

Пример из жизни

Негативный кейс

Код выбрасывал исключения в деструкторах ресурсов; при аварийном завершении стек развёртывался некорректно, ресурсы остались утечкой.

Плюсы:

  • Возможно быстрое обнаружение ошибок

Минусы:

  • Программы часто выходили с ошибкой terminate

Позитивный кейс

Критичные методы были помечены noexcept, деструкторы обрабатывали все исключения внутри себя. Применялись catch по ссылке, все ошибки логировались.

Плюсы:

  • Надёжность, отсутствие leaks
  • Крайне предсказуемое поведение

Минусы:

  • Повышенная строгость к разработчику при написании корректного, "чистого" кода