Для некоторых языков (например, C ++) утечка ресурсов не должна быть причиной
C ++ основан на RAII.
Если у вас есть код, который может дать сбой, вернуть или выбросить (то есть наиболее нормальный код), тогда вам следует поместить указатель внутрь умного указателя (при условии, что у вас есть очень веская причина не иметь объект, созданный в стеке).
Коды возврата более подробны
Они многословны и имеют тенденцию развиваться во что-то вроде:
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
В конце концов, ваш код представляет собой набор идентифицированных инструкций (я видел такой код в производственном коде).
Этот код можно было бы перевести на:
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
Которые четко разделяют код и обработку ошибок, что может быть хорошо.
Коды возврата более хрупкие
Если не какое-то неясное предупреждение от одного компилятора (см. Комментарий phjr), их можно легко проигнорировать.
В приведенных выше примерах предположим, что кто-то забыл обработать свою возможную ошибку (это происходит ...). При возврате ошибка игнорируется и, возможно, позже взорвется (т. Е. Указатель NULL). Такой же проблемы не будет, за исключением.
Ошибка не будет проигнорирована. Но иногда хочется, чтобы он не взорвался ... Так что выбирать нужно внимательно.
Коды возврата иногда необходимо переводить
Допустим, у нас есть следующие функции:
- doSomething, который может возвращать int с именем NOT_FOUND_ERROR
- doSomethingElse, который может возвращать логическое значение false (для ошибки)
- doSomethingElseAgain, который может возвращать объект Error (как с __LINE__, __FILE__, так и с половиной переменных стека.
- doTryToDoSomethingWithAllThisMess, который, ну ... Используйте вышеуказанные функции и возвращайте код ошибки типа ...
Каков тип возврата doTryToDoSomethingWithAllThisMess, если одна из его вызываемых функций не работает?
Коды возврата - не универсальное решение
Операторы не могут вернуть код ошибки. Конструкторы C ++ тоже не могут.
Коды возврата означают, что вы не можете связывать выражения
Следствие из вышеизложенного. Что, если я хочу написать:
CMyType o = add(a, multiply(b, c)) ;
Я не могу, потому что возвращаемое значение уже используется (а иногда его нельзя изменить). Таким образом, возвращаемое значение становится первым параметром, отправленным как ссылка ... Или нет.
Исключение набирается
Вы можете отправлять разные классы для каждого типа исключения. Исключения ресурсов (т.е. нехватка памяти) должны быть легкими, но все остальное может быть настолько тяжелым, насколько это необходимо (мне нравится, что исключение Java дает мне весь стек).
Затем каждый улов может быть специализирован.
Никогда не используйте catch (...) без повторного броска
Обычно не стоит скрывать ошибку. Если вы не перебрасываете, по крайней мере, зарегистрируйте ошибку в файле, откройте окно сообщения, что угодно ...
Исключение составляют ... NUKE
Проблема с исключением заключается в том, что их чрезмерное использование приведет к созданию кода, полного попыток / уловок. Но проблема в другом: кто пытается / поймать свой код с помощью контейнера STL? Тем не менее, эти контейнеры могут отправлять исключение.
Конечно, в C ++ никогда не позволяйте исключению выйти из деструктора.
Исключение составляют ... синхронные
Обязательно поймайте их, прежде чем они поставят вашу нить на колени или распространятся внутри вашего цикла сообщений Windows.
Решением может быть их смешивание?
Поэтому я думаю, что решение - бросить, когда чего-то не должно случиться. А когда что-то может случиться, используйте код возврата или параметр, чтобы пользователь мог на это отреагировать.
Итак, единственный вопрос: чего не должно происходить?
Это зависит от контракта вашей функции. Если функция принимает указатель, но указывает, что указатель должен быть отличным от NULL, тогда нормально генерировать исключение, когда пользователь отправляет указатель NULL (вопрос в том, в C ++, когда автор функции не использовал ссылки вместо этого указателей, но ...)
Другое решение - показать ошибку
Иногда ваша проблема в том, что вам не нужны ошибки. Использование исключений или кодов возврата ошибок - это круто, но ... Вы хотите знать об этом.
В своей работе мы используем что-то вроде Assert. Это будет, в зависимости от значений файла конфигурации, независимо от параметров компиляции отладки / выпуска:
- записать ошибку
- откройте почтовый ящик, нажав "Привет, у вас проблема"
- откройте окно сообщения с помощью Эй, у вас проблема, вы хотите отладить
Как при разработке, так и при тестировании это позволяет пользователю точно определить проблему, когда она обнаружена, а не после (когда некоторый код заботится о возвращаемом значении или внутри ловушки).
Его легко добавить в унаследованный код. Например:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
ведет своего рода код, похожий на:
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(У меня есть похожие макросы, которые активны только при отладке).
Обратите внимание, что на производстве файл конфигурации не существует, поэтому клиент никогда не видит результат этого макроса ... Но его легко активировать, когда это необходимо.
Вывод
Когда вы кодируете с использованием кодов возврата, вы готовитесь к неудаче и надеетесь, что ваша крепость тестов достаточно безопасна.
Когда вы кодируете с использованием исключения, вы знаете, что ваш код может дать сбой, и обычно ставите контрфайер в выбранную стратегическую позицию в вашем коде. Но обычно ваш код больше касается того, что он должен делать, чем того, что, как я боюсь, произойдет.
Но когда вы вообще кодируете, вы должны использовать лучший инструмент, который есть в вашем распоряжении, и иногда это Никогда не скрывайте ошибку и показывайте ее как можно скорее. Макрос, о котором я говорил выше, следует этой философии.
person
paercebal
schedule
21.09.2008