Какой правильный подход для обработки ошибок в C ++ - PullRequest
9 голосов
/ 25 июня 2010

Одним из них является использование исключений в C ++: попробуйте перехватить блоки. Но освобождение динамической памяти будет проблемой при возникновении исключения.

Второй - использовать стиль C: переменная errno

Третье - просто вернуть -1 в случае ошибки и 0 в случае успеха:)

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

Ответы [ 8 ]

28 голосов
/ 25 июня 2010

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

Нет, это не так.std::vector<int> v(100); Готово.

Концепция здесь называется Scope-Bound Resource Management (SBRM), также известной под гораздо более распространенным (и неудобным) названием Resource Acquisition Is Initialization (RAII).По сути, все ресурсы содержатся в каком-либо объекте, который очистит ресурс в деструкторе (который всегда гарантированно будет запускаться для автоматически распределенного объекта).Таким образом, независимо от того, существует ли функция нормально или через исключение, деструктор запускается, и ваш ресурс очищается.

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

4 голосов
/ 25 июня 2010

Второй - использовать стиль C: переменная errno

Третье - просто вернуть -1 в случае ошибки и 0 в случае успеха:)

И как они помогают решить вашу проблему освобождения динамической памяти? Они также используют стратегию раннего выхода, такую ​​же как throw.

Итак, в итоге, они не имеют преимущества перед исключениями C ++ (по вашему мнению).

3 голосов
/ 27 июня 2010

Одним из них является использование исключений C ++: попробуйте блокировать catch.Но освобождение динамической памяти будет проблемой при возникновении исключения.

@ see RAII.

Исключения должны быть вашим предпочтительным методом решения исключительных ситуаций времени выполнения, таких как нехватка памяти,Обратите внимание, что что-то вроде std :: map :: find не выдает (и не должно), потому что это не обязательно ошибка или особенно исключительный случай для поиска ключа, который не существует: функция может сообщить клиенту,или нет ключ существует.Это не нарушение предусловия или постусловия, как требование наличия файла для правильной работы программы и обнаружение, что файла там нет.

Красота обработки исключений, есливы делаете это правильно (опять же, @see RAII), то есть избегаете необходимости засорять код обработки ошибок по всей вашей системе.

Давайте рассмотрим случай, когда функция A вызывает функцию B, которая вызывает C, а затем D ии так далее, вплоть до «Z».Z - единственная функция, которая может генерировать, а A - единственная, заинтересованная в восстановлении после ошибки (A - точка входа для операции высокого уровня, например, такой как загрузка изображения).Если вы придерживаетесь RAII, который будет полезен не только для обработки исключений, то вам нужно всего лишь поместить строку кода в Z, чтобы вызвать исключение, и небольшой блок try / catch в A, чтобы перехватить исключение и, скажем, отобразитьсообщение об ошибке пользователю.

К сожалению, многие люди не придерживаются RAII так строго, как следовало бы на практике, поэтому во многих реальных кодах больше блоков try / catch, чем необходимо дляиметь дело с ручной очисткой ресурса (которая не должна быть ручной).Тем не менее, это идеал, которого вы должны стремиться достичь в своем коде, и он более практичен, если это проект среднего размера.Аналогично, в реальных сценариях люди часто игнорируют коды ошибок, возвращаемые функциями.если вы собираетесь приложить дополнительные усилия в пользу надежности, вы также можете начать с RAII, потому что это поможет вашему приложению независимо от того, используете ли вы обработку исключений или обработку кода ошибки.

Существует предупреждение: вы не должны выбрасывать исключения через границы модуля.Если вы это сделаете, вы должны рассмотреть гибрид между кодами ошибок (как при возврате кодов ошибок, не используя глобальный статус ошибки, например, errno) и исключениями.

Стоит отметить, что если вы используете оператор new в своем коде, не указав nothrow везде, например:

int* p = new int(123); // can throw std::bad_alloc
int* p = new(std::nothrow) int(123); // returns a null pointer on failure

... тогда вам уже нужно отлавливать и обрабатывать исключения bad_alloc в вашемкод для обеспечения его устойчивости к исключениям нехватки памяти.

3 голосов
/ 25 июня 2010

Прежде всего, вы должны стремиться к программе с минимальным количеством ошибок. (Потому что ошибки не крутые.)

Исключения - хороший инструмент, но следует использовать консервативно : зарезервируйте их для "исключительных случаев", не используйте их для контроля потока вашей программы.

Например, не используйте исключения для проверки правильности ввода пользователя. (В таком случае верните код ошибки.)

2 голосов
/ 25 июня 2010

Посмотрите на этот комментарий Херба Саттера о try catch для C ++ GOTW .И пройти через весь его набор статей.Ему действительно есть, что сказать, когда и как проверить себя и избавиться от ошибок, а также как наилучшим образом справиться с ними.

1 голос
/ 25 июня 2010

Исключения хороши для передачи управления из одного контекста в другой.
Вы позволяете компилятору выполнить развертывание стека между контекстами, а затем в новом контексте компенсировать исключение (а затем, надеюсь, продолжить).

Если ваша ошибка возникает и может быть исправлена ​​в том же контексте, то коды ошибок являются хорошим методом для обработки ошибок и их устранения (не принимайте это, чтобы означать, что вы не должны использовать RAII, вам все еще нужно это) Но, например, внутри класса возникает ошибка в функции, и вызывающая функция может исправить ошибку такого типа (тогда это, вероятно, не исключительное обстоятельство, поэтому нет исключений), тогда код ошибки полезен.

Вы не должны использовать коды ошибок, когда вам приходится передавать информацию из библиотеки или подсистемы, поскольку вы полагаетесь на то, что разработчик использует код для фактической проверки и обработки кода, чтобы убедиться, что он работает правильно и чаще, чем не они будут игнорировать коды ошибок.

1 голос
/ 25 июня 2010

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

Освобождение памяти (или любого другого ресурса в этом отношении) внезапно не станет проблемой, потому чтоВы не используете исключения.Методы, которые облегчают решение этих проблем, в то время как исключения могут быть упрощены, также облегчают, когда могут быть «условия ошибки».

1 голос
/ 25 июня 2010

Брось исключение.Деструкторы переменных всегда вызываются, когда выдается исключение, и если ваши основанные на стеке переменные не очищаются после себя (если, например, вы использовали необработанный указатель, когда вам нужно удалить результат), то вы получите то, что заслуживаете,Используйте умные указатели, без утечек памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...