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

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

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

Третий - это просто вернуть -1 при ошибке и 0 при успешном выполнении:)

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

Ответ 1

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

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

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

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

Ответ 2

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

Третий - это просто вернуть -1 при ошибке и 0 при успешном выполнении:)

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

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

Ответ 3

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

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

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

Ответ 4

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

@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) и исключения.

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

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 в вашем коде, чтобы они были надежными в отношении исключений из памяти.

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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