Каков правильный подход для обработки ошибок в C++

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

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

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

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

9
задан Kromster says support Monica 4 June 2014 в 04:46
поделиться

8 ответов

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

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

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

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

28
ответ дан 4 December 2019 в 06:11
поделиться

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

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

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

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

4
ответ дан 4 December 2019 в 06:11
поделиться

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

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

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

3
ответ дан 4 December 2019 в 06:11
поделиться

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

1
ответ дан 4 December 2019 в 06:11
поделиться

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

2
ответ дан 4 December 2019 в 06:11
поделиться

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

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

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

1
ответ дан 4 December 2019 в 06:11
поделиться

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

@ см. 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
ответ дан 4 December 2019 в 06:11
поделиться

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

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

1
ответ дан 4 December 2019 в 06:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: