Какой из следующих двух catch
блоков легче понять:
try {
do_something();
}
catch (const int&) {
// WTF did I catch?
}
catch (const std::out_of_range&) {
// Oh, something was out of range!
}
Имя класса исключения должно рассказать вам о том, почему возникло исключение; int
ничего вам не говорит, вы просто знаете, что поймали int
, что бы это ни значило.
Чтобы рассмотреть ваш обновленный пример использования перечисления вместо целого числа, что из следующего яснее:
try{
do_something();
}
// (1) Catch an enum:
catch (MyException e) {
switch(e) {
except_a: break;
except_b: break;
except_c: break;
default: throw; // Don't forget, you have to throw any exceptions
// that you caught but don't actually want to catch!
}
}
// (2) Catch specific exceptions
catch (const ExceptionA&) { }
catch (const ExceptionB&) { }
catch (const ExceptionC&) { }
Нет никаких оснований отдавать предпочтение первой форме: нет никакого выигрыша в производительности, а код менее понятен и более подвержен ошибкам. В вашем примере вы забыли перебросить исключение, если не обрабатывали его, поэтому, если бы кто-то позже добавил исключение except_d
в перечисление MyException
, вы бы по незнанию его поймали.
Что касается вашего "накладного" вопроса, это зависит, но, вероятно, нет. Должно быть (относительно) редко, чтобы генерировалось исключение, и если у вас узкий цикл, где производительность действительно имеет значение, вы все равно не будете использовать исключения.
Преимущество использования иерархий классов для исключений состоит в том, что они позволяют писать более понятный код, так же как преимущество использования нелокального потока управления (исключений) вместо других подходов, таких как возвращаемые значения кода ошибки, позволяет писать более понятный код (по крайней мере, если вы правильно используете исключения).