Насколько я понимаю, в современном C++ предпочтительнее использовать throw
вместо возврата кода ошибки. То есть, если у меня есть какая-то функция:
void MyFunction(int foo, double bar){
// Do some stuff....
if (exception_criteria_met){
throw "Call to MyFunction with inputs" << foo << " and " << bar << " failed because ...";
}
};
Означает ли это, что для правильной обработки этого во всех функциях, которые вызывают MyFunction
, я должен реализовать что-то вроде:
void MyOuterFunction(int foo){
// Do some stuff...
try {
MyFunction(foo, bar);
} catch (const char * exception_message) {
throw "Call to MyOuterFunction with inputs " << foo << " failed because call to MyFunction with ....";
};
И тогда не будет ли это означать, что я должен выполнять такую же обработку ошибок на всем пути вниз через все функции, которые вызывают этот стек вызовов функций? Очевидно, что в какой-то момент одна из функций может действительно обрабатывать исключение и не должна создавать собственное исключение, а, возможно, просто напечатать предупреждение или что-то подобное. Но как сделать это организованным и понятным? Кажется, что очень сложно все продумать, когда у вас много функций, взаимодействующих друг с другом.
(В качестве второстепенного вопроса, если бы MyFunction(foo,bar)
вернул значение, которое я хотел использовать, не означало бы это, что после выхода из блока try { }
возвращаемое значение выпало бы из области видимости? Означает ли это, что я должен избегать использования возвращаемых функцией значений? если я буду использовать try-catch
?)
вы должны перехватывать исключение, когда это имеет смысл перехватывать, например, когда вы можете осмысленно восстановиться после него. Затем вы восстанавливаетесь от него, и весь код вверх по стеку вызовов даже не замечает, что произошло исключение. Ловля и повторный бросок редко бывают полезными.
Вы разрешаете исключениям раскручивать стек до тех пор, пока функция не поймает его и не обработает. не требуется немедленно перехватывать исключение в коде вызывающего объекта. вы можете сделать это в вызывающем абоненте. Также спорны преимущества и недостатки исключений. то же самое о правильном месте их использования.
если вам нужно поймать исключение из функции в вызывающей функции, вы также можете использовать коды возврата ошибок. Все дело в том, что исключения не обязательно ловить сразу.
В целом нельзя сказать, что генерация исключений лучше, чем возврат кода ошибки. Это все равно, что сказать, что молоток лучше отвертки. Возможно, но это зависит от решаемой проблемы.
Если вы не используете C++23 для std::expected
, есть результат Boost и результат.
Я думаю, что большая часть вашего вопроса либо слишком широка, либо основана на мнении, либо и то, и другое. Однако ваше замешательство, кажется, начинается с
то во всех функциях, которые вызывают MyFunction, я должен реализовать что-то вроде:
Это не правильно.
Функция, вызывающая MyFunction
, которая потенциально выдает бросок, может выглядеть так:
void MyOuterFunction(int foo){
// Do some stuff...
MyFunction(foo, bar);
};
Если бы вам приходилось всегда немедленно перехватывать исключение, исключения были бы довольно непрактичными. Они не так работают.
В двух словах: исключение продолжает перемещаться вверх по стеку вызовов до тех пор, пока вы его не поймаете или не достигнете main
, и оно все еще не будет перехвачено, в этом случае программа завершается.
Нет фиксированного правила, по которому вы должны перехватывать исключение. Мое личное эмпирическое правило — поймать его, когда можно оправиться от него, но не раньше. Иногда не уловить ожидание вообще является наиболее жизнеспособным. Поймать и снова бросить редко бывает полезно, а ловить только для того, чтобы снова бросить, как ты это делаешь, никогда не бывает полезно, насколько мне известно.
С другой стороны, поймать и бросить как нечто другое может быть очень полезно при пересечении границы библиотеки.
My understanding is that in modern C++, it is preferable to use throw instead of returning an error code.
Не совсем так. Похоже, мы возвращаемся к возвращению ошибок, а не их выдаче, см., например, std::expected