Являются ли перегрузки операторов в C ++ более сложными, чем они того стоят? - PullRequest
6 голосов
/ 01 апреля 2009

По моему опыту преподавания C ++, перегрузка операторов является одной из тех тем, которая вызывает наибольшее горе у студентов. Даже глядя на вопросы здесь в stackoverflow: например, сделать оператор + внешним или членом? Как справиться с симметрией и т. Д., Кажется, это много проблем.

Когда я переходил с C ++ на Java, я волновался, что упущу эту возможность, но, за исключением таких операторов, как [] или (), у меня никогда не возникало необходимости перегружать операторы. На самом деле, я чувствую, что программы без них более читабельны.

Примечание: я поставил это как вики сообщества. Давайте обсудим это. Я хочу услышать мнения.

Ответы [ 14 ]

38 голосов
/ 02 апреля 2009

Перегруженные операторы как специи. Немного может сделать что-то лучше; слишком много может сделать его неприятным.

30 голосов
/ 02 апреля 2009

Некоторые примеры перегрузки, о которых должен знать каждый программист C ++, даже если они не одобряют:

  • operator = () требуется для того, чтобы объекты C ++ могли вести себя как значения.
  • оператор -> () требуется для реализации умных указателей
  • operator << () и operator >> () необходимы для интеграции типов в инфраструктуру iostream
  • operator <() используется по умолчанию при сравнении объектов, хранящихся в стандартных контейнерах библиотеки </li>
  • operator () () используется для реализации функторов, используемых стандартными библиотечными алгоритмами
  • Оператор ++ (), как ожидается, будет доступен, если вы реализуете свой собственный итератор
14 голосов
/ 02 апреля 2009

Как легко жаловаться на перегруженных операторов, если они не действуют удивительным образом, я действительно не вижу проблемы. Да, есть плохие примеры (даже в STL). Смотрите оператор присваивания auto_ptr, например. Перегрузка некоторых операторов, таких как &&, || и ,, почти всегда будет плохой. Но по большей части заставьте операторов делать то, что они рекламируют, и в этом нет реальной проблемы.

Плохая практика перегружать operator+ делать что-то странное, но это так же плохо, если вы добавляете метод «Add» в ваш класс, который сериализует объект на диск.

6 голосов
/ 02 апреля 2009

Перегруженные операторы - потенциально превосходные способы делать определенные вещи, но их ужасно легко злоупотреблять.

Перегрузка операторов << и >> позволяет легко расширять потоки C ++ как в новых видах потоков, так и в новых объектах для ввода-вывода, и в обоих случаях. Перегрузка -> делает умные указатели практически заменой указателей C ++. Перегруженные операторы позволяют иметь оператор конкатенации строк и создавать новые виды чисел, которые синтаксически аналогичны int s. Их наличие позволяет делать в библиотеках такие вещи, которые требуют изменения уровня языка в других языках.

У них есть свои ограничения. Нет оператора, подходящего для возведения в степень. Существует только один оператор умножения, а в некоторых случаях есть несколько способов умножения (например, для трехмерных векторов есть по крайней мере точки и перекрестные произведения). Операторы &&, || и запятая не могут реплицировать свои встроенные функции, поскольку они не могут иметь оценки короткого замыкания и точки последовательности.

И, конечно, ими можно злоупотреблять. Например, нет требования к языку, что арифметические операторы должны работать так же, как арифметические. Я видел ужасные вещи, сделанные в попытке придумать нотацию SQL, которую кто-то считал интуитивно понятной. В плохо написанной программе на C ++ невозможно знать, что, скажем, a = x * y; делает, поскольку это a.operator=(x.operator*(y));, или, может быть, a.operator=(operator*(x, y)); или что-то еще, и операторные функции могут быть написаны так, чтобы делать что угодно.

Намерение Бьярна Страуструпа при разработке C ++ заключалось в том, чтобы включить полезные функции независимо от возможности злоупотребления, тогда как намерение Джеймса Гослинга при разработке Java состояло в том, чтобы исключить чрезмерно злоупотребляемые функции, даже если они были несколько полезны. Мне не ясно, является ли любая из этих философий правильной или неправильной, но они разные.

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

Что касается обучения студентов, скажите им не перегружать самих операторов (за исключением определенных условий, таких как функторы и оператор присваивания), но укажите, как в библиотеке используются перегруженные операторы. Я бы не стал доверять любому студенту C ++, который сделал бы их правильно, и если они будут в состоянии сделать это, они могут и изучат это самостоятельно. Они будут знать, что это сложно, потому что вы запретили это в классе. Некоторые из тех, которым я бы никогда не доверял ничего более сложного, чем оператор for, узнают, как перегрузить операторы, и все равно это сделают, но это жизнь.

5 голосов
/ 02 апреля 2009

Перегрузка оператора довольно важна для многих целей. Функторы невозможно было бы создать без возможности перегрузки оператора (). Общее программирование во многих случаях стало бы болью в заднице. Если я пишу числовой алгоритм, я полагаюсь на то, что тип значения ведет себя одинаково, будь то тип float, double, std :: complex или какой-то самодельный тип. Я полагаюсь на определяемые обычные арифметические операторы и тому подобное, поэтому мне не нужно писать отдельную перегрузку для встроенных типов, а другую - для пользовательских.

Умные указатели полагаются на объекты, способные перегрузить оператор разыменования, чтобы они могли вести себя как указатели.

Перегрузка операторов чрезвычайно важна для того, чтобы сделать программирование на С ++ сносным. Что касается сложности, я просто не вижу этого. Это не сложнее, чем создать свою собственную функцию, которую люди обычно находят довольно легкой. Если вы называете это «умножить», то это функция, если вы называете это «оператор *», это оператор. Но код в теле точно такой же.

Конечно, операторы иногда подвергаются насилию. И << или >> могут быть приемлемыми для границы, но они настолько широко известны и используются, что я думаю, что это справедливо.

Если бы вы спросили о перегрузке операторов в чем-то вроде C #, я бы с удовольствием обошелся без них. Их реализация гораздо более неловкая, они не работают с обобщениями и не поддерживают все приятные и удобные приемы, которые использует C ++.

4 голосов
/ 02 апреля 2009

Я думаю, что это действительно зависит от варианта использования. Существует несколько типов классов, для которых необходимы операторы. Например, умные указатели были бы бесполезны без операторов -> и *.

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

if ( point1 > point2 ) ...

Просто выглядит лучше, чем

if ( point1.Compare(point2) < 0 ) ...

Я нахожу меньшее применение для других операторов, хотя иногда полезно использовать приведение.

3 голосов
/ 02 апреля 2009

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

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

2 голосов
/ 02 апреля 2009

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

1 голос
/ 02 апреля 2009

Мне очень нравится возможность перегрузки арифметических операторов для не встроенных типов в C ++. Но только для типов с арифметическим поведением; например, классы с фиксированной точкой, трехмерные векторные классы, классы комплексных чисел, классы "bignum" произвольной длины. Я написал подобный код на Java и был раздражен необходимостью писать что-то вроде a.Add(b) вместо a+b. Имейте в виду, я математик по образованию; Мне кажется, что перегрузка операторов позволяет вам получить немного хорошего в языке C ++, не требуя его реализации.

Но меня действительно раздражает, когда, например, operator+ перегружен функциональностью, которую лучше всего сделать с помощью operator<< (следуя хитрому, но хорошо известному соглашению iostream) или шаблонам, подобным STL .push_back().

Что касается operator() ... после открытия boost::bind & boost::function Я не представляю жизнь без функторов. А умные указатели были бы не такими удобными без перегрузки operator*, operator-> и т. Д.

1 голос
/ 02 апреля 2009

Операторы и случаи, когда я их использовал:
operator->, operator * - для прокси-объектов и различных оболочек.
оператор = - необходим для предотвращения непредвиденного поведения при копировании.
оператор <(>, <=,> =) - для хранения в карте или наборе (но обычно лучше передать функтор в этот).
оператор << (>>) - для потоков и boost :: lexical_cast совместимость.
оператор ==,! = - для разрешения сравнения объектов.
оператор! - иногда вместо функции valid ().
Тип оператора - для преобразования в другой тип.
operator () - для интеллектуального функтора, когда буст был запрещен.

вот и все.
Иногда назад я использовал другие операторы, но это было для моих математических утилит.

Также следует быть осторожным с логическими операторами (&&, ||) - у нас будет разница со стандартной семантикой:

ptr && ptr->method()

может иметь другой смысл, если мы перегружаем оператор &&.

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