Перегрузка оператора - Operator overloading

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

Обоснование

Перегрузка оператора есть синтаксический сахар, и используется, потому что он позволяет программировать с использованием нотации, близкой к целевой области[1] и предоставляет определяемым пользователем типам такой же уровень синтаксической поддержки, что и типы, встроенные в язык. Это распространено, например, в научных вычислениях, где позволяет манипулировать вычислительными представлениями математических объектов с тем же синтаксисом, что и на бумаге.

Перегрузка оператора не меняет выразительная сила языка (с функциями), поскольку его можно эмулировать с помощью вызовов функций. Например, рассмотрим переменные а, б и c определенного пользователем типа, например матрицы:

а + б * в

На языке, который поддерживает перегрузку операторов, и с обычным предположением, что оператор '*' имеет большее значение приоритет чем оператор '+', это краткий способ написания:

Сложить (a, Умножить (b, c))

Однако первый синтаксис отражает обычное математическое использование.

Примеры

В этом случае оператор сложения перегружается, чтобы разрешить добавление для определенного пользователем типа. Время в C ++:

Время оператор+(const Время& lhs, const Время& rhs) {  Время темп = lhs;  темп.секунды += rhs.секунды;  темп.минут += темп.секунды / 60;  темп.секунды %= 60;  темп.минут += rhs.минут;  темп.часы += темп.минут / 60;  темп.минут %= 60;  темп.часы += rhs.часы;  возвращаться темп;}

Дополнение - это бинарная операция, что означает наличие двух операнды. В C ++ передаваемые аргументы - это операнды, а темп объект - это возвращаемое значение.

Операция также может быть определена как метод класса, заменив lhs скрытым это аргумент; Однако это заставляет левый операнд иметь тип Время:

// Эта «константа» означает, что | это | не модифицируется.//  //   ------------------------------------//                                       |// VВремя Время::оператор+(const Время& rhs) const {  Время темп = *это;  // | это | не должны быть изменены, поэтому сделайте копию.  темп.секунды += rhs.секунды;  темп.минут += темп.секунды / 60;  темп.секунды %= 60;  темп.минут += rhs.минут;  темп.часы += темп.минут / 60;  темп.минут %= 60;  темп.часы += rhs.часы;  возвращаться темп;}

Обратите внимание, что унарный оператор, определенный как метод класса, не получит очевидного аргумента (он работает только с это):

bool Время::оператор!() const {  возвращаться часы == 0 && минут == 0 && секунды == 0;}

Оператор меньше (<) часто перегружается для сортировки структуры или класса:

учебный класс Пара { общественный:  bool оператор<(const Пара& п) const {    если (Икс_ == п.Икс_) {      возвращаться y_ < п.y_;    }    возвращаться Икс_ < п.Икс_;  } частный:  int Икс_;  int y_;};

Как и в предыдущих примерах, в последнем примере перегрузка оператора выполняется внутри класса. В C ++ после перегрузки оператора «меньше» (<) стандартные функции сортировки можно использовать для сортировки некоторых классов.

Критика

Перегрузка оператора часто подвергается критике[2] потому что он позволяет программистам переназначать семантику операторов в зависимости от типов их операндов. Например, использование << оператор в C ++:

а << б

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

Другая, более тонкая проблема с операторами заключается в том, что некоторые математические правила могут быть неверно ожидаемы или непреднамеренно приняты. Например, коммутативность из + (т.е. а + Ь == Ь + а) не всегда применяется; пример этого происходит, когда операнды являются строками, поскольку + обычно перегружается для выполнения конкатенации строк (т.е. "птица" + "песня" дает "пение птиц", пока "песня" + "птица" дает "певчая птица"). Типичный счетчик[нужна цитата ] Этот аргумент исходит непосредственно из математики: хотя + коммутативен для целых чисел (и, в более общем смысле, для любого комплексного числа), он не коммутативен для других «типов» переменных. На практике + даже не всегда ассоциативный, например, со значениями с плавающей запятой из-за ошибок округления. Другой пример: в математике умножение коммутативно для действительных и комплексных чисел, но не коммутативно для матричное умножение.

Каталог

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

ОператорыНе перегруженПерегружаемый
Новый определяемый[4]
Ограниченный набор

Хронология перегрузки оператора

1960-е

В АЛГОЛ 68 спецификация допускала перегрузку оператора.[37]

Выдержка из спецификации языка ALGOL 68 (стр. 177), где перегруженные операторы ¬, =, ≠ и пресс определены:

10.2.2. Операции над логическими операндамиa) op ∨ = (bool а, б) bool:( а | истинный | б); б) op ∧ = (bool а, б) bool: (a | b | ложный ); в) op ¬ = (bool а) bool: (а | ложный | истинный ); г) op = = (bool а, б) bool:( a∧b) ∨ (¬b∧¬a); e) op ≠ = (bool а, б) bool: ¬ (a = b); f) op пресс = (bool а)int: (а | 1 | 0);

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

1980-е

Ада поддерживает перегрузку операторов с самого начала, с публикацией языкового стандарта Ada 83. Однако разработчики языка предпочли исключить определение новых операторов. Только существующие операторы в языке могут быть перегружены путем определения новых функций с такими идентификаторами, как «+», «*», «&» и т. Д. Последующие версии языка (в 1995 и 2005 гг.) Сохраняют ограничение на перегрузку существующих операторов. .

В C ++, перегрузка операторов более тонкая, чем в АЛГОЛ 68.[38]

1990-е годы

Ява языковые дизайнеры в Sun Microsystems решил опустить перегрузку.[39][40][41]

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

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

2000-е

Microsoft добавила перегрузку операторов в C # в 2001 году и до Visual Basic .NET в 2003 г.

Scala обрабатывает все операторы как методы и, таким образом, допускает перегрузку операторов через прокси.

В Раку, определение всех операторов делегируется лексическим функциям, и поэтому, используя определения функций, операторы могут быть перегружены или добавлены новые операторы. Например, функция, определенная в Ракудо источник для увеличения объекта Date со знаком «+»:

мульти инфикс:<+>(Дата: D $ d, Инт: D $ x) {    Дата.новичок($ d.дневной счет + $ x)}

Поскольку использовалось "multi", функция добавляется в список мультидиспетч кандидаты, а "+" перегружается только в том случае, если выполняются ограничения типа в сигнатуре функции. В то время как возможность перегрузки включает +, *, >=, то постфикс и термин я и так далее, он также позволяет перегрузить различные операторы фигурных скобок: "[х, у]", "Икс[ у ]", "Икс{ у }", и" x( у )".

Котлин поддерживает перегрузку операторов с момента его создания.

Смотрите также

Рекомендации

  1. ^ Страуструп, Бьярне. «Перегрузка оператора». C ++ FAQ. Архивировано из оригинал 14 августа 2011 г.. Получено 27 августа 2020.
  2. ^ Фишер, Чарльз Н. (2008). «Проблемы с перегрузкой» (PDF). Университет Висконсина-Мэдисона.
  3. ^ «Больше никаких перегрузок оператора». Языковая среда Java. Корпорация Oracle.
  4. ^ Могут быть добавлены совершенно новые операторы.
  5. ^ Бинарные функции с символическим именем можно называть инфиксными.
  6. ^ "Предикат op / 3".
  7. ^ Хант, Джон (6 декабря 2012 г.). Smalltalk и объектная ориентация: введение. Springer Science & Business Media. ISBN  978-1-4471-0961-7.
  8. ^ Представлен в Fortran 90.
  9. ^ "3. Справочник по языку - документация Futhark 0.19.0". futhark.readthedocs.io. Получено 10 октября 2020.
  10. ^ Смит, Крис (9 октября 2012 г.). Программирование F # 3.0: подробное руководство по написанию простого кода для решения сложных проблем. O'Reilly Media, Inc. ISBN  978-1-4493-2604-3.
  11. ^ Типовые классы вместо перегрузки.
  12. ^ «Создание операторов».
  13. ^ «Операторы». Экскурсия по Скале.
  14. ^ «Руководство по Seed7: определение структурированного синтаксиса». seed7.sourceforge.net. Получено 29 сентября 2020.
  15. ^ «Swift: продвинутые операторы».
  16. ^ «Почему Go не поддерживает перегрузку методов и операторов?». Получено 4 сентября 2011.
  17. ^ "Вступление". www.freepascal.org. Получено 30 сентября 2020.
  18. ^ «Перегрузка оператора». Получено 28 сентября 2018.
  19. ^ «6.6 Перегрузка операторов». Аннотированное справочное руководство по Ada.
  20. ^ Дрейтон, Питер; Альбахари, Бен; Ньюард, Тед (2003). C # в двух словах. O'Reilly Media, Inc. ISBN  978-0-596-00526-9.
  21. ^ «Перегрузка оператора C ++».
  22. ^ «Перегрузка оператора - язык программирования D». dlang.org. Получено 10 октября 2020.
  23. ^ «Экскурсия по языку Дарт». dart.dev. Получено 30 сентября 2020.
  24. ^ «Язык программирования Apache Groovy - Операторы». groovy-lang.org. Получено 30 сентября 2020.
  25. ^ «Перегрузка оператора». Многообразие. Получено 7 июн 2020.
  26. ^ «Перегрузка оператора». Котлин. Получено 24 июн 2018.
  27. ^ "Учебное пособие по метаметодам". Lua-пользователи Wiki.
  28. ^ «Реализация операторов для вашего класса». Получено 1 октября 2013.
  29. ^ «Перегрузка оператора». Руководство Free Pascal. Получено 1 декабря 2014.
  30. ^ «Перегрузка оператора». Руководство по Delphi. Получено 1 декабря 2014.
  31. ^ «Магические методы PHP, переопределяющие свойства класса». Архивировано из оригинал 4 марта 2016 г.. Получено 7 апреля 2015.
  32. ^ Орвант, Джон (4 ноября 2002 г.). Компьютерные науки и программирование на Perl: лучшее из журнала Perl. O'Reilly Media, Inc., стр. 347–. ISBN  978-0-596-00310-4.
  33. ^ «3. Модель данных». Справочник по языку Python.
  34. ^ «Методы». Официальный Ruby FAQ.
  35. ^ «Перегрузка оператора». Ржавчина на примере.
  36. ^ «Как: определить оператор (Visual Basic)».
  37. ^ Вейнгаарден, Адриан; Майю, Барри Дж.; Пек, Джон Э. Л.; Костер, Корнелис Х. А.; и другие. (Август 1968 г.). «Отчет по алгоритмическому языку АЛГОЛ 68, раздел 10.2.2» (PDF). Получено 1 апреля 2007.
  38. ^ Страуструп, Бьярне. «История C ++: 1979–1991» (PDF). п. 12. Получено 1 апреля 2007.
  39. ^ "FAQ Вопрос 6.9: Почему нет перегрузки оператора?". Список часто задаваемых вопросов о comp.lang.java.
  40. ^ "java.sun.com".
  41. ^ Хольцнер, Стивен (2001). C ++: Черная книга. Скоттсдейл, Аризона: Coriolis Group. п. 387. ISBN  1-57610-777-9. Одна из самых приятных особенностей C ++ ООП заключается в том, что вы можете перегружать операторы для обработки объектов ваших классов (вы не можете сделать это в некоторых других языках, ориентированных на ООП, таких как Java).