Если мы говорим о внутренней политике обработки ошибок приложения / модуля C ++, то мое личное мнение таково:
Q: Исключают ли исключения коды ошибок полностью или, возможно, мне нужно использоватьих только для "смертельных случаев"?
A: Они делают.Исключения всегда лучше для функции C ++, чем возврат кодов ошибок.Причина объясняется ниже.
Q: Является ли смешивание двух парадигм (исключений и кодов ошибок) хорошей идеей?
A: НетСмешивание является уродливым и делает обработку ошибок несовместимой.Когда я вынужден использовать API, возвращающий ошибки (например, Win32 API, POSIX и т. Д.), Я использую оболочки, генерирующие исключения.
Q: Является ли хорошей идеей предоставить пользователю две концепции?
A: Нет. Пользователи не понимают, какой вариант выбрать, и обычно принимают худшее решение, смешивая оба варианта.Некоторые пользователи предпочитают исключения, другие предпочитают возвращать ошибки, и если все они работают над одним проектом, они делают практику обработки ошибок проекта полным беспорядком.
Q: Есть ли хорошие примерыКонцепция смешивания исключений и кодов ошибок?
A: Нет. Покажите мне, если найдете.IMO, изолирующий функции возврата ошибок с обертками, генерирующими исключения, является наилучшей практикой, если вам нужно использовать функции, возвращающие ошибки (и вам обычно приходится их использовать).
Q: Как бы выреализовать это?
A: Я бы использовал только исключения.Мой путь возвращается только в случае успеха.Практика возврата ошибок сильно путает код с ветвями проверки ошибок или, что еще хуже, проверка состояния ошибок отсутствует, и поэтому статус ошибок просто игнорируется, что делает код полным скрытых ошибок, которые трудно обнаружить.Исключения делают обработку ошибок изолированной.Если вам нужно обработать какую-то ошибку на месте, это обычно означает, что это вовсе не ошибка, а просто какое-то законное событие, о котором можно сообщить при успешном возврате с указанием определенного состояния (по возвращаемому значению или иным образом).Если вам действительно нужно проверить, произошла ли какая-либо ошибка локально (не из корневого блока try / catch), вы можете попробовать / перехватить локально, поэтому использование только исключений на самом деле не ограничивает ваши возможности.
Важное примечание:
Для каждой конкретной ситуации очень важно правильно определить, что является ошибкой, а что нет (для лучшего удобства использования).
Например, у нас есть функция, котораяпоказывает диалог ввода и текст возврата, введенный пользователем, и если пользователь может отменить ввод, то событие отмены будет успешным - не ошибка (но при возврате пользователь должен как-то указать, что ввод был отменен), но нехватка ресурсов (таких как память или GDIобъекты или что-то) или что-то вроде отсутствия монитора, чтобы показать диалоговое окно действительно ошибка.
В целом:
Исключения являются более естественным механизмом обработки ошибок дляЯзык С ++Поэтому использование исключений - хорошая идея, если вы разрабатываете приложение или библиотеку C ++ для использования только приложением C ++ (не приложением C и т. Д.).Возврат ошибок - более переносимый подход - вы можете возвращать коды ошибок в приложения, написанные на любом языке программирования и даже работающие на другом компьютере.Конечно, почти всегда запросы ОС сообщают о своем состоянии с помощью кодов ошибок (потому что естественно сделать их независимыми от языка).И по этой причине вам приходится иметь дело с кодами ошибок в повседневном программировании. НО Политика планирования ошибок IMO приложения C ++, основанная на кодах ошибок, просто напрашивается на неприятности - приложение превращается в совершенно нечитаемый беспорядок.IMO лучший способ справиться с кодами состояния в приложении C ++ - это использовать функции / классы / методы-оболочки C ++ для вызова функций, возвращающих ошибки, а если возвращается ошибка - генерировать исключение (с информацией о состоянии, встроенной в класс исключения).
Некоторые важные замечания и предостережения:
Чтобы использовать исключения в качестве политики обработки ошибок в проекте (большом или маленьком), важно иметь строгую политику написания безопасного кода для исключения.По сути, это означает, что каждый ресурс получен в конструкторе некоторого класса и, что более важно, выпущен в деструкторе (это гарантирует, что у вас нет утечек ресурсов).Кроме того, вы должны где-то перехватывать исключения - обычно в вашей функции корневого уровня (например, main
или оконной процедуре или процедуре потока и т. Д.).
Рассмотрите этот код:
SomeType* p = new SomeType;
some_list.push_back(p);
/* later element of some_list have to be delete-ed
after removing them from this list */
Это типичная потенциальная утечка памяти - если push_back генерирует исключение, то динамически размещаемый и создаваемый объект SomeType просачивается.
Безопасный вариант исключения является следующим:
std::auto_ptr<SomeType> pa( new SomeType );
some_list.push_back(pa.get());
pa.release();
/* later elements of some_list have to be delete-ed
after removing them from this list */
Или:
boost::shared_ptr<SomeType> pa( new SomeType );
some_list.push_back(pa);
/* some_list is list of boost::shared_ptr<SomeType>
so everything is delete-ed automatically */
Если вы используете стандартные шаблоны C ++, распределители и т. Д., Вы либо пишете безопасный код исключения (если вы пытаетесь / перехватываете каждый отдельный вызов STL, код становится беспорядочным), либо оставляете код полным потенциальных утечек ресурсов (то естьк сожалению бывает очень часто).Истинное приложение C ++ должно быть безопасным для исключений.