Для некоторых языков (например, 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, который может возвращать bool "false" (для сбойного)
- doSomethingElseSagain, которая может возвращать объект Error (как с __LINE__, __FILE__, так и с половиной переменных стека.
- doTryToDoSomethingWithAllThisMess, который, ну ... Используйте вышеупомянутые функции и возвращают код ошибки типа ...
Каков тип возврата doTryToDoSomethingWithAllThisMess в случае сбоя одной из вызванных функций?
Коды возврата не являются универсальным решением
Операторы не могут вернуть код ошибки. C ++ конструкторы тоже не могут.
Коды возврата означают, что вы не можете связывать выражения
Следствие вышеприведенного пункта. Что делать, если я хочу написать:
CMyType o = add(a, multiply(b, c)) ;
Не могу, потому что возвращаемое значение уже используется (и иногда его нельзя изменить). Таким образом, возвращаемое значение становится первым параметром, отправляемым как ссылка ... Или нет.
Исключение набирается
Вы можете отправлять разные классы для каждого вида исключений. Исключения Ressources (т.е. нехватка памяти) должны быть легкими, но все остальное может быть настолько тяжелым, насколько это необходимо (мне нравится исключение Java, дающее мне весь стек).
Каждый улов может быть специализированным.
Никогда не используйте catch (...) без повторного броска
Обычно вы не должны скрывать ошибку. Если вы не выбрасываете повторно, по крайней мере, зарегистрируйте ошибку в файле, откройте окно сообщения, что угодно ...
Исключением являются ... NUKE
Проблема с исключением состоит в том, что чрезмерное их использование приведет к созданию кода, полного try / catches. Но проблема в другом: кто пытается / ловит его / ее код, используя контейнер STL? Тем не менее, эти контейнеры могут отправлять исключения.
Конечно, в C ++ никогда не позволяйте исключению выходить из деструктора.
Исключение составляют ... синхронные
Обязательно поймайте их, пока они не вывели вашу нить на колени, или не распространились внутри цикла сообщений Windows.
Решением может быть их смешивание?
Так что я думаю, что решение состоит в том, чтобы бросить, когда что-то должно не произойти. А когда что-то может произойти, используйте код возврата или параметр, чтобы пользователь мог реагировать на это.
Итак, единственный вопрос: «что-то, что не должно происходить?»
Это зависит от контракта вашей функции. Если функция принимает указатель, но указывает, что указатель должен быть ненулевым, то можно генерировать исключение, когда пользователь отправляет указатель 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.
}
(у меня есть похожие макросы, которые активны только при отладке).
Обратите внимание, что на производстве файл конфигурации не существует, поэтому клиент никогда не увидит результат этого макроса ... Но его легко активировать при необходимости.
Заключение
Когда вы кодируете, используя коды возврата, вы готовите себя к неудаче и надеетесь, что ваша крепость тестов достаточно безопасна.
Когда вы кодируете, используя исключение, вы знаете, что ваш код может потерпеть неудачу, и обычно помещаете встречный улов в выбранную стратегическую позицию в вашем коде. Но обычно ваш код больше о том, «что он должен делать», а не о том, что, боюсь, произойдет.
Но когда вы вообще кодируете, вы должны использовать лучший инструмент в вашем распоряжении, и иногда это «Никогда не скрывайте ошибку и показывайте ее как можно скорее». Макрос, о котором я говорил выше, следует этой философии.