Для некоторых языков (например, 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 ++, когда автор функции не использовал ссылки вместо этого указателей, но ...)
Другое решение - показать ошибку
Иногда ваша проблема в том, что вам не нужны ошибки. Использование исключений или кодов возврата ошибок - это круто, но ... Вы хотите знать об этом.
В своей работе мы используем своего рода «Утверждение». В зависимости от значений файла конфигурации независимо от параметров компиляции отладки / выпуска:
- записать ошибку
- откройте окно сообщения с надписью "Привет, у вас проблема"
- откройте окно сообщения с надписью «Привет, у вас проблема, вы хотите отладить»
И при разработке, и при тестировании это позволяет пользователю точно определить проблему, когда она обнаружена, а не после (когда некоторый код заботится о возвращаемом значении или внутри улова).
Его легко добавить в унаследованный код. Например:
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.
}
(У меня есть похожие макросы, которые активны только при отладке).
Обратите внимание, что на производстве файл конфигурации не существует, поэтому клиент никогда не видит результат этого макроса ... Но его легко активировать, когда это необходимо.
Вывод
Когда вы кодируете с использованием кодов возврата, вы готовитесь к неудаче и надеетесь, что ваша крепость тестов достаточно безопасна.
Когда вы пишете код с использованием исключения, вы знаете, что ваш код может дать сбой, и обычно ставите контрфайер в выбранную стратегическую позицию в вашем коде. Но обычно ваш код больше о том, «что он должен делать», чем о том, «что, как я боюсь, произойдет».
Но когда вы вообще пишете код, вы должны использовать лучший инструмент, который есть в вашем распоряжении, и иногда это «Никогда не скрывайте ошибку и показывайте ее как можно скорее». Макрос, о котором я говорил выше, следует этой философии.