2010-06-25 3 views
9

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

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

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

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

+8

Используйте исключение для ИСКЛЮЧИТЕЛЬНЫХ ОШИБКОВ (http://stackoverflow.com/questions/180937/are-exceptions-really-for-exceptional-errors). Для обычных и повторяющихся ошибок вы можете попробовать новые объекты system_error в C++ 0x http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html. Это ускорение. – anno

+0

Вы должны использовать оба варианта. Какой из них зависит от ситуации. см. http://stackoverflow.com/questions/106586/what-are-the-principles-guiding-your-exception-handling-policy/106749#106749 –

+0

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

ответ

28

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

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

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

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

+1

Согласен в целом, хотя я бы избегал использовать «никогда» в таком руководстве - для каждого правила есть исключения. Опыт заключается в том, чтобы узнать, где эти исключения имеют смысл. – Stabledog

+3

@Stable: «для каждого правила есть исключения» - это правило? В любом случае, помимо написания самого контейнера ресурсов, вы никогда не должны будете использовать контейнер. Никогда не имеет смысла ставить себя в положение, чтобы быть очень осторожным в том, чтобы забыть что-то удалить, его всегда можно автоматизировать в деструкторе. – GManNickG

+0

Есть очень мало исключений, если вы используете C++, когда дело доходит до RAII. Все ресурсы, выделенные в классе, должны быть освобождены к моменту уничтожения экземпляра класса. Все распределения памяти и освобождения памяти должны выполняться внутри классов, за исключением распределителей памяти, которые все равно должны использоваться для выделения и освобождения памяти внутри класса. Для безопасности исключений RAII не следует рассматривать как вариант; это обязательно. – stinky472

4

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

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

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

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

+0

Как изменяется, какое значение получает возвращенный мандат раннего выхода? 'int func() {int returnValue = 0; ... (может установить returnValue для других значений); return returnValue; } ' – Bill

+0

@Bill: хорошо, что используется единственная точка выхода конвенции, которая в значительной степени вне моды, и не без оснований. На самом деле это не улучшает поток управления. Разумеется, он также может применяться с исключениями C++. На самом деле никакой разницы. Просто дополнительная переменная, необходимая для кэширования исключения для броска. –

1

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

2

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

3

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

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

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

1

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

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

1

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

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

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

3

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

@see RAII.

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

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

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

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

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

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

Смежные вопросы