Обработка ошибок - хитрый зверь в лучшие времена. Это в значительной степени сводится к тому, насколько серьезна ошибка, и что, если с ней что-то случится, когда она произойдет.
Вы можете следовать четырем основным путям:
- Брось исключение
- Кувалда обработки ошибок. Отличный инструмент, определенно хотите использовать его, если он вам нужен, но если вы не будете осторожны, вы в конечном итоге разбьетесь о ногу.
- По существу пропускает все между
throw
и catch
, не оставляя после себя ничего, кроме смерти и разрушения.
- Если она не перехвачена, она прервет вашу программу.
- Возвращает значение, которое указывает на ошибку
- Оставьте это на усмотрение программиста, чтобы проверить успешность и реагировать соответствующим образом.
- Значение ошибки будет зависеть от типа. Указатели могут возвращать
NULL
или 0
, контейнеры STL возвращают object.end()
, в противном случае могут использоваться неиспользуемые значения (например, -1
или ""
).
- Обработать условие изящно
- Иногда ошибка на самом деле не ошибка, а просто неудобство.
- Если полезные результаты все еще можно получить, ошибку можно легко заметить, не причиняя никому вреда.
- Например, ошибка вне диапазона может просто вернуть последнюю переменную в массиве, не прибегая к каким-либо беспорядочным исключениям.
- Пока это предсказуемо и определены , программист может сделать из этого то, что они хотят.
- Неопределенное поведение
- Эй, программисты не должны давать вам плохой ввод. Пусть они страдают.
В общем, я бы прибегнул к первому варианту только для вещей, которые нарушают работу программы, для вещей, от которых я действительно не ожидаю восстановиться без согласованных усилий. В противном случае использование исключений в качестве формы управления потоком немного лучше, чем возвращение к временам goto
.
Второй вариант, вероятно, наиболее распространен для ошибок, не нарушающих работу программы, но его эффективность действительно зависит от типов возврата, с которыми вы имеете дело. Это выгодно, поскольку позволяет программисту контролировать поток локально, выявляя сбои и восстанавливая себя. Когда речь идет о перегрузке операторов, она имеет ограниченное применение, но я решила, что добавлю ее ради полноты.
Вариант три очень специфичен для конкретных обстоятельств. Многие ошибки не могут быть обработаны таким образом, и даже те, которые могут привести к неинтуитивным результатам. Используйте с осторожностью и обязательно документ тщательно . Или вообще не документируйте это, а притворяйтесь, что это четвертый вариант.
Теперь, что касается конкретного предоставленного примера, что из-за ошибки вне диапазона для перегруженного operator[]
, я бы лично выбрал четвертый вариант. Не потому, что мне особенно нравится наблюдать, как другие программисты страдают, когда они имеют дело с моим кодом (да, кстати, но это имеет отношение к обсуждению), а потому, что это ожидается.
В большинстве случаев, когда программист будет использовать operator[]
, они ожидают, что будут обрабатывать свои собственные проверки границ и не полагаются на тип или класс, чтобы что-то сделать для них. Даже в контейнерах STL вы можете видеть operator[]
(без проверки диапазона) параллельно с избыточным object.at()
(который выполняет проверку диапазона). Отражение ожидаемого поведения с вашими перегруженными операторами приводит к более интуитивному коду.