В компьютерном программировании перегрузка операторов , иногда называемая специальным полиморфизмом операторов , является частным случаем полиморфизма , когда разные операторы имеют разные реализации в зависимости от своих аргументов. Перегрузка операторов обычно определяется языком программирования , программистом или обоими.
Перегрузка операторов является синтаксическим сахаром и используется потому, что она позволяет программировать с использованием нотации, более близкой к целевой области [1] , и обеспечивает определяемым пользователем типам тот же уровень синтаксической поддержки, что и типам, встроенным в язык. Это распространено, например, в научных вычислениях, где позволяет манипулировать вычислительными представлениями математических объектов с тем же синтаксисом, что и на бумаге.
Перегрузка операторов не меняет выразительных возможностей языка (с функциями), поскольку ее можно эмулировать с помощью вызовов функций. Например, рассмотрим переменные a
некоторого b
определяемого c
пользователем типа, например матрицы :
a + b * c
На языке, поддерживающем перегрузку операторов, и при обычном предположении, что оператор «*» имеет более высокий приоритет , чем оператор «+», это краткий способ записи:
Add(a, Multiply(b, c))
Однако первый синтаксис отражает обычное математическое использование.
В этом случае оператор сложения перегружается, чтобы разрешить добавление пользовательского типа Time
в C++ :
Оператор времени + ( const Time & lhs , const Time & rhs ) { Time temp = lhs ; темп . секунды += правая шкала . секунды ; темп . минуты += темп . секунды / 60 ; темп . секунды %= 60 ; темп . минуты += правое час . минуты ; темп . часы += темп . минут / 60 ; темп . минуты %= 60 ; темп . часы += правая шкала . часы ; температура возврата ; }
Сложение является бинарной операцией , что означает, что оно имеет два операнда . В C++ передаваемые аргументы являются операндами, а temp
объект — возвращаемым значением.
Операцию также можно определить как метод класса, заменив ее lhs
скрытым this
аргументом; Однако это приводит к тому, что левый операнд будет иметь тип Time
:
// "const" прямо перед открывающей фигурной скобкой означает, что |this| не модифицируется. Время Время :: оператор + ( const Time & rhs ) const { Time temp = * this ; // |это| не следует изменять, поэтому сделайте копию. темп . секунды += правая шкала . секунды ; темп . минуты += темп . секунды / 60 ; темп . секунды %= 60 ; темп . минуты += правое час . минуты ; темп . часы += темп . минут / 60 ; темп . минуты %= 60 ; темп . часы += правая шкала . часы ; температура возврата ; }
Обратите внимание, что унарный оператор, определенный как метод класса, не получит видимого аргумента (он работает только с this
):
bool Время :: оператор ! () const { возвращаем часы == 0 && минуты == 0 && секунды == 0 ; }
Оператор «меньше» (<) часто перегружается для сортировки структуры или класса:
class Pair { public : bool оператор < ( const Pair & p ) const { if ( x_ == p . x_ ) { return y_ < p . й_ ; } Верните x_ < p . Икс_ ; } личное : int x_ ; интервал y_ ; };
Как и в предыдущих примерах, в последнем примере перегрузка операторов выполняется внутри класса. В C++ после перегрузки оператора «меньше» (<) для сортировки некоторых классов можно использовать стандартные функции сортировки .
Перегрузку операторов часто критикуют [2] , поскольку она позволяет программистам переназначать семантику операторов в зависимости от типов их операндов. Например, использование оператора <<
в C++ сдвигает биты в переменной на биты, если и имеют целочисленный тип, но если это выходной поток, то приведенный выше код попытается записать в поток. Поскольку перегрузка операторов позволяет первоначальному программисту изменить обычную семантику оператора и застать врасплох последующих программистов, считается хорошей практикой использовать перегрузку операторов с осторожностью (создатели Java решили не использовать эту возможность, [3] хотя и не обязательно по этой причине).a << b
a
b
a
b
a
b
Другая, более тонкая проблема с операторами заключается в том, что некоторые математические правила могут быть ошибочно ожидаемы или непреднамеренно приняты. Например, коммутативность + (т.е. что a + b == b + a
) не всегда применима; пример этого происходит, когда операнды являются строками, поскольку + обычно перегружается для выполнения конкатенации строк (т.е. "bird" + "song"
Gives "birdsong"
, while "song" + "bird"
Gives "songbird"
). Типичное противодействие этому аргументу приходит непосредственно из математики: хотя + коммутативен для целых чисел (и, в более общем случае, для любого комплексного числа), он не коммутативен для других «типов» переменных. На практике + даже не всегда ассоциативен , например, со значениями с плавающей запятой из-за ошибок округления. Другой пример: в математике умножение коммутативно для действительных и комплексных чисел, но не коммутативно при умножении матриц .
Классификация некоторых распространенных языков программирования производится по тому, могут ли их операторы перегружаться программистом и ограничены ли операторы заранее определенным набором.
Спецификация АЛГОЛа 68 допускала перегрузку операторов. [44]
Выдержка из спецификации языка ALGOL 68 (стр. 177), где определены перегруженные операторы ¬, =, ≠ и abs :
10.2.2. Операции с логическими операндамиа) op ∨ = ( bool a, b) bool :( a | true | b );б) op ∧ = ( bool a, b) bool : (a | b | false );в) op ¬ = ( bool a) bool : (a | false | true );d) op = = ( bool a, b) bool :( a∧b ) ∨ ( ¬b∧¬a );д) op ≠ = ( bool a, b) bool : ¬(a=b);е) op abs = ( bool a) int : (a | 1 | 0);
Обратите внимание, что для перегрузки оператора не требуется специального объявления , и программист может создавать новые операторы. Для диадических операторов можно установить их приоритет по сравнению с другими операторами:
прио макс = 9; op max = ( int a, b) int : (a>b | a | b); op ++ = ( ref int a ) int : ( a +:= 1 );
Ada поддерживает перегрузку операторов с момента своего создания, после публикации языкового стандарта Ada 83. Однако разработчики языка решили исключить определение новых операторов. Только существующие в языке операторы могут быть перегружены путем определения новых функций с такими идентификаторами, как «+», «*», «&» и т. д. Последующие версии языка (в 1995 и 2005 годах) сохраняют ограничение на перегрузку существующих операторов. .
В C++ перегрузка операторов более совершенна, чем в ALGOL 68 . [45]
Разработчики языка Java в Sun Microsystems решили отказаться от перегрузки. [46] [47] [48]
Python позволяет перегружать операторы посредством реализации методов со специальными именами. [49] Например, оператор сложения (+) можно перегрузить, реализовав метод obj.__add__(self, other)
.
Ruby позволяет перегрузку операторов в качестве синтаксического сахара для простых вызовов методов.
Lua позволяет перегрузку операторов в качестве синтаксического сахара для вызовов методов с дополнительной функцией: если первый операнд не определяет этот оператор, будет использоваться метод для второго операнда.
Microsoft добавила перегрузку операторов в C# в 2001 году и в Visual Basic .NET в 2003 году.
Scala рассматривает все операторы как методы и, таким образом, допускает перегрузку операторов через прокси.
В Raku определение всех операторов делегировано лексическим функциям, поэтому с помощью определений функций можно перегружать операторы или добавлять новые операторы. Например, функция, определенная в исходном коде Rakudo для увеличения объекта Date с помощью «+», следующая:
мультиинфикс :<+> ( Date:D $d , Int:D $x ) { Date . новое количество дней ( $d . daycount + $x )}
Поскольку использовалось «multi», функция добавляется в список кандидатов на множественную отправку , а «+» перегружается только в том случае, если соблюдаются ограничения типа в сигнатуре функции. Хотя возможность перегрузки включает + , * , >= , постфикс и термин i и т. д., она также позволяет перегружать различные операторы фигурных скобок: " [ x, y ] ", "x [ y ] ", "x { y } » и «x ( y ) ».
Котлин поддерживает перегрузку операторов с момента своего создания.
{{cite web}}
: Проверить |url=
значение ( помощь )Одна из самых приятных особенностей ООП C++ заключается в том, что вы можете перегружать операторы для обработки объектов ваших классов (вы не можете сделать это в некоторых других ООП-ориентированных языках, таких как Java).