Исключения и обработка ошибок
Генерация исключения
throw передаёт управление механизму обработки ошибок:
#include <stdexcept>int Divide(int a, int b) { if (b == 0) { throw std::invalid_argument("Деление на ноль"); } return a / b;}Исключение подходит для ситуации, когда функция не может выполнить заявленную операцию и обычный возвращаемый результат не выражает ошибку достаточно ясно.
try и catch
try { std::cout << Divide(10, 0) << '\n';} catch (const std::invalid_argument& error) { std::cerr << "Ошибка: " << error.what() << '\n';}Перехватывай стандартные исключения по const-ссылке, чтобы не копировать объект и не срезать производный тип.
Проброс исключения
Внутри catch выражение throw; повторно выбрасывает текущее исключение. Это полезно, когда текущий уровень может выполнить очистку или логирование, но не умеет полностью обработать ошибку.
RAII и исключения
При раскрутке стека локальные объекты уничтожаются. Поэтому ресурсы нужно хранить в RAII-объектах, а не полагаться на ручной код после операции, которая может выбросить исключение.
Когда исключения не нужны
Для ожидаемых ситуаций вроде «пользователь ввёл неверную команду» часто достаточно вернуть статус или std::optional. Для результата, который может быть значением или ошибкой, в C++23 существует std::expected. Выбор зависит от стандарта проекта и принятого стиля.
Не делай так
- Не используй пустой
catch (...), который молча скрывает все ошибки. - Не выбрасывай исключения из деструкторов при обычном уничтожении.
- Не используй исключения как замену нормальному ветвлению в каждой итерации.
Итог
Определи, какие ошибки ожидаемы, а какие означают невозможность выполнить операцию. Обеспечивай безопасность ресурсов с RAII и обрабатывай исключения на уровне, где есть решение для пользователя или программы.
