Исключения и обработка ошибок
Сообщайте о сбоях аккуратно, восстанавливайтесь безопасно и держите объекты в согласованном состоянии
throw объекта-ошибки; затем язык разматывает стек, пока не найдётся подходящий catch. Если сделать это правильно, основной ("счастливый") путь кода остаётся чистым, а ресурсы освобождаются даже при сбое.
- правильно использовать
throw,tryиcatchи бросать объекты, производные отstd::exception, - объяснить разматывание стека и почему именно RAII делает исключения безопасными,
- рассуждать о трёх гарантиях безопасности исключений (базовая, строгая, без исключений) и помечать код
noexcept, - решать, когда использовать исключения, а когда предпочесть коды ошибок, такие как
std::optionalилиstd::error_code.
1. Почему не просто возвращать код ошибки?
Любая нетривиальная функция может дать сбой: файл не открывается, кончается память, ввод пользователя бессмыслен, чтение из сети завершается по тайм-ауту. Сообщить об этом вызывающему можно двумя основными способами.
- Коды ошибок / особые значения: функция возвращает специальное значение (
-1,nullptr,false,enum), которое вызывающий обязан проверить. - Исключения: функция делает
throwобъекта; обычное выполнение останавливается, и управление переходит к обработчику.
Беда кодов ошибок в том, что их легко проигнорировать. Ничто не заставляет вызывающего смотреть на возвращаемое значение, поэтому забытая проверка тихо портит программу. Ещё они засоряют каждое место вызова проверками if и не могут сообщить о сбое там, где нет канала возврата, - например, из конструктора или из перегруженного оператора. Исключения решают это: необработанное исключение нельзя тихо потерять (вместо этого программа завершается), а тип ошибки несёт с собой сообщение и может нести данные.