c ++: перегрузка операторов и обработка ошибок - PullRequest
3 голосов
/ 05 декабря 2009

В настоящее время я начинаю изучать перегрузку операторов в c ++ для простого 2D-класса вершин, где позиция должна быть доступна с оператором []. Обычно это работает, но я действительно не знаю, как обращаться с ошибками, например, если оператор выходит за пределы (в случае двумерного класса вершин, который имеет только значения x и y, он выходит за пределы, если он больше, чем один)

Каков общий способ обработки ошибок в подобных случаях?

Спасибо

Ответы [ 6 ]

4 голосов
/ 05 декабря 2009

Когда вам нужно throw, вам нужно throw. Нет другого способа диагностировать проблему в перегруженном операторе, если только вы не можете вернуть какое-то магическое взрывное значение результата. Определите тип исключения, выдавайте его при ошибках, документируйте его.

2 голосов
/ 06 декабря 2009

Обработка ошибок - хитрый зверь в лучшие времена. Это в значительной степени сводится к тому, насколько серьезна ошибка, и что, если с ней что-то случится, когда она произойдет.

Вы можете следовать четырем основным путям:

  1. Брось исключение
    • Кувалда обработки ошибок. Отличный инструмент, определенно хотите использовать его, если он вам нужен, но если вы не будете осторожны, вы в конечном итоге разбьетесь о ногу.
    • По существу пропускает все между throw и catch, не оставляя после себя ничего, кроме смерти и разрушения.
    • Если она не перехвачена, она прервет вашу программу.
  2. Возвращает значение, которое указывает на ошибку
    • Оставьте это на усмотрение программиста, чтобы проверить успешность и реагировать соответствующим образом.
    • Значение ошибки будет зависеть от типа. Указатели могут возвращать NULL или 0, контейнеры STL возвращают object.end(), в противном случае могут использоваться неиспользуемые значения (например, -1 или "").
  3. Обработать условие изящно
    • Иногда ошибка на самом деле не ошибка, а просто неудобство.
    • Если полезные результаты все еще можно получить, ошибку можно легко заметить, не причиняя никому вреда.
    • Например, ошибка вне диапазона может просто вернуть последнюю переменную в массиве, не прибегая к каким-либо беспорядочным исключениям.
    • Пока это предсказуемо и определены , программист может сделать из этого то, что они хотят.
  4. Неопределенное поведение
    • Эй, программисты не должны давать вам плохой ввод. Пусть они страдают.

В общем, я бы прибегнул к первому варианту только для вещей, которые нарушают работу программы, для вещей, от которых я действительно не ожидаю восстановиться без согласованных усилий. В противном случае использование исключений в качестве формы управления потоком немного лучше, чем возвращение к временам goto.

Второй вариант, вероятно, наиболее распространен для ошибок, не нарушающих работу программы, но его эффективность действительно зависит от типов возврата, с которыми вы имеете дело. Это выгодно, поскольку позволяет программисту контролировать поток локально, выявляя сбои и восстанавливая себя. Когда речь идет о перегрузке операторов, она имеет ограниченное применение, но я решила, что добавлю ее ради полноты.

Вариант три очень специфичен для конкретных обстоятельств. Многие ошибки не могут быть обработаны таким образом, и даже те, которые могут привести к неинтуитивным результатам. Используйте с осторожностью и обязательно документ тщательно . Или вообще не документируйте это, а притворяйтесь, что это четвертый вариант.


Теперь, что касается конкретного предоставленного примера, что из-за ошибки вне диапазона для перегруженного operator[], я бы лично выбрал четвертый вариант. Не потому, что мне особенно нравится наблюдать, как другие программисты страдают, когда они имеют дело с моим кодом (да, кстати, но это имеет отношение к обсуждению), а потому, что это ожидается.

В большинстве случаев, когда программист будет использовать operator[], они ожидают, что будут обрабатывать свои собственные проверки границ и не полагаются на тип или класс, чтобы что-то сделать для них. Даже в контейнерах STL вы можете видеть operator[] (без проверки диапазона) параллельно с избыточным object.at() (который выполняет проверку диапазона). Отражение ожидаемого поведения с вашими перегруженными операторами приводит к более интуитивному коду.

2 голосов
/ 05 декабря 2009

Согласно FAQ по языку C ++, operator[] должен не использоваться для реализаций матриц или 2d-массивов; вместо этого используйте operator(). Нажмите здесь для FAQ # 13.10

Большой проблемой является реализация [] для нескольких измерений.

Что касается ошибок, вам придется перейти на маршрут исключение , если вы не хотите предоставлять какие-либо дополнительные параметры перегруженному оператору (еще одна причина использовать operator().

1 голос
/ 06 декабря 2009

У вас есть как минимум две опции, кроме исключений, для обработки индексов за пределами:

  • Просто доверяйте своим данным, документируйте, что это неопределенное поведение, чтобы использовать индекс вне границ, и полагайтесь на своих абонентов как профессионалов [*].
  • Прервать, если индекс выходит за границы, вызывая std::terminate(), или abort() напрямую, или что-то еще, возможно, после печати сообщения об ошибке.

Между ними есть компромисс, который заключается в использовании макроса assert. Это будет сделано в первых сборках выпуска (скомпилированных с NDEBUG), а второе - в отладочных сборках.

Не то чтобы исключения - это обязательно плохая идея, но у них есть свои проблемы. Опять же, большинство из этих проблем исчезнет, ​​если вы их никогда не поймаете.

В этом случае звонящий должен передать вам либо 0, либо 1. Если они иногда пропускают вас 2 и планируют поймать исключение, которое происходит, когда они это делают, у них может не быть надежды. Не тратьте слишком много времени на беспокойство.

Другой вариант - принять все входные данные, но сопоставить их с тем или иным значением. Например, вы можете побитировать - и ввод с 1. Это делает ваш код очень простым, с очевидным недостатком, заключающимся в том, что он скрывает ошибки других людей.

[*] Нельзя сказать, что профессионалы не делают ошибок. Они делают. Они просто не ожидают, что вы спасете их от их ошибок.

1 голос
/ 06 декабря 2009

Как отмечали другие, исключения - это путь.

Но, похоже, это довольно необычная идиома для доступа к точечному классу. Я считаю, что для вершинного класса гораздо проще иметь отдельные члены:

class Vertex {
    ...
    double x;
    double y;
};

Затем вы можете оперировать ими, выполняя такие действия, как vertex1.x - vertex2.x и т. Д., Что IMO более читабельно, чем vertex1[0] - vertex2[0]. Для дополнительного бонуса он полностью исключает проблему исключений.

1 голос
/ 06 декабря 2009

Я думаю, что утверждение также может быть на месте. Предвидите ли вы, что когда-нибудь, кроме (простой?) Ошибки программиста, выйдет за пределы двумерного вектора?

T& operator[](size_t index)
{
    assert(index < 2 && "Vector index out of bounds");
    return pos[index];
}

Если вы собираетесь генерировать исключения, я полагаю, вы также можете использовать out_of_range - или тип, производный от него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...