Sobrecarga do operador - Operator overloading
Polimorfismo |
---|
Polimorfismo ad hoc |
Polimorfismo paramétrico |
Subtipagem |
Na programação de computadores , a sobrecarga de operador , às vezes chamada de polimorfismo ad hoc de operador , é um caso específico de polimorfismo , em que diferentes operadores têm implementações diferentes dependendo de seus argumentos. A sobrecarga do operador é geralmente definida por uma linguagem de programação , um programador ou ambos.
Justificativa
A sobrecarga de operador é um açúcar sintático e é usado porque permite a programação usando a notação mais próxima do domínio de destino e permite aos tipos definidos pelo usuário um nível semelhante de suporte sintático aos tipos construídos em uma linguagem. É comum, por exemplo, na computação científica, onde permite que representações computacionais de objetos matemáticos sejam manipuladas com a mesma sintaxe do papel.
A sobrecarga de operador não altera o poder expressivo de uma linguagem (com funções), pois pode ser emulada por meio de chamadas de função. Por exemplo, considere as variáveis a
, b
e c
de algum tipo definido pelo usuário, tais como matrizes :
a + b * c
Em uma linguagem que suporta sobrecarga de operador e com a suposição usual de que o operador '*' tem precedência mais alta do que o operador '+', esta é uma maneira concisa de escrever:
Add(a, Multiply(b, c))
No entanto, a sintaxe anterior reflete o uso matemático comum.
Exemplos
Nesse caso, o operador de adição está sobrecarregado para permitir a adição em um tipo definido pelo usuário Time
em C ++ :
Time operator+(const Time& lhs, const Time& rhs) {
Time temp = lhs;
temp.seconds += rhs.seconds;
temp.minutes += temp.seconds / 60;
temp.seconds %= 60;
temp.minutes += rhs.minutes;
temp.hours += temp.minutes / 60;
temp.minutes %= 60;
temp.hours += rhs.hours;
return temp;
}
A adição é uma operação binária , o que significa que possui dois operandos . Em C ++, os argumentos passados são os operandos e o temp
objeto é o valor retornado.
A operação também pode ser definida como um método de classe, substituindo lhs
pelo this
argumento oculto ; No entanto, isso força o operando esquerdo a ser do tipo Time
:
// The "const" right before the opening curly brace means that |this| is not modified.
Time Time::operator+(const Time& rhs) const {
Time temp = *this; // |this| should not be modified, so make a copy.
temp.seconds += rhs.seconds;
temp.minutes += temp.seconds / 60;
temp.seconds %= 60;
temp.minutes += rhs.minutes;
temp.hours += temp.minutes / 60;
temp.minutes %= 60;
temp.hours += rhs.hours;
return temp;
}
Observe que um operador unário definido como um método de classe não receberia nenhum argumento aparente (ele só funciona this
):
bool Time::operator!() const {
return hours == 0 && minutes == 0 && seconds == 0;
}
O operador menor que (<) geralmente é sobrecarregado para classificar uma estrutura ou classe:
class Pair {
public:
bool operator<(const Pair& p) const {
if (x_ == p.x_) {
return y_ < p.y_;
}
return x_ < p.x_;
}
private:
int x_;
int y_;
};
Como nos exemplos anteriores, no último exemplo, a sobrecarga do operador é feita dentro da classe. Em C ++, após sobrecarregar o operador menor que (<), as funções de classificação padrão podem ser usadas para classificar algumas classes.
Críticas
A sobrecarga do operador tem sido freqüentemente criticada porque permite que os programadores reatribuam a semântica dos operadores dependendo dos tipos de seus operandos. Por exemplo, o uso do <<
operador em C ++ desloca os bits na variável deixados por bits se e forem de um tipo inteiro, mas se for um fluxo de saída, o código acima tentará escrever um no fluxo. Como a sobrecarga de operador permite que o programador original altere a semântica usual de um operador e pegue quaisquer programadores subsequentes de surpresa, é considerada uma boa prática usar a sobrecarga de operador com cuidado (os criadores do Java decidiram não usar esse recurso, embora não necessariamente por esta razão).
a << b
a
b
a
b
a
b
Outro problema mais sutil com os operadores é que certas regras da matemática podem ser erroneamente esperadas ou assumidas não intencionalmente. Por exemplo, a comutatividade de + (ou seja, que a + b == b + a
) nem sempre se aplica; um exemplo disso ocorre quando os operandos são strings, uma vez que + é comumente sobrecarregado para realizar uma concatenação de strings (ou seja , "bird" + "song"
rendimentos "birdsong"
, enquanto "song" + "bird"
rendimentos "songbird"
). Um contador típico para este argumento vem diretamente da matemática: embora + seja comutativo em inteiros (e mais geralmente qualquer número complexo), não é comutativo para outros "tipos" de variáveis. Na prática, + nem sempre é associativo , por exemplo, com valores de ponto flutuante devido a erros de arredondamento. Outro exemplo: em matemática, a multiplicação é comutativa para números reais e complexos, mas não comutativa na multiplicação de matrizes .
Catálogo
Uma classificação de algumas linguagens de programação comuns é feita de acordo com se seus operadores são sobrecarregados pelo programador e se os operadores estão limitados a um conjunto predefinido.
Operadores | Não sobrecarregável | Sobrecarregável |
---|---|---|
Novo definível | ||
Conjunto limitado |
Linha do tempo de sobrecarga do operador
Década de 1960
A especificação ALGOL 68 permitia sobrecarga do operador.
Extraia da especificação de linguagem ALGOL 68 (página 177) onde os operadores sobrecarregados ¬, =, ≠ e abs são definidos:
10.2.2. Operations on Boolean Operands a) op ∨ = (bool a, b) bool:( a | true | b ); b) op ∧ = (bool a, b) bool: ( a | b | false ); c) op ¬ = (bool a) bool: ( a | false | true ); d) op = = (bool a, b) bool:( a∧b ) ∨ ( ¬b∧¬a ); e) op ≠ = (bool a, b) bool: ¬(a=b); f) op abs = (bool a)int: ( a | 1 | 0 );
Observe que nenhuma declaração especial é necessária para sobrecarregar um operador, e o programador está livre para criar novos operadores.
Década de 1980
Ada suporta a sobrecarga de operadores desde o seu início, com a publicação do padrão de linguagem Ada 83. No entanto, os designers da linguagem optaram por impedir a definição de novos operadores. Apenas os operadores existentes na linguagem podem ser sobrecarregados, definindo novas funções com identificadores como "+", "*", "&" etc. As revisões subsequentes da linguagem (em 1995 e 2005) mantêm a restrição à sobrecarga dos operadores existentes .
Em C ++ , a sobrecarga de operador é mais refinada do que em ALGOL 68 .
Década de 1990
Os designers de linguagem Java da Sun Microsystems optaram por omitir a sobrecarga.
Ruby permite a sobrecarga de operador como açúcar sintático para chamadas de método simples.
Lua permite a sobrecarga de operadores como açúcar sintático para chamadas de método com o recurso adicional de que, se o primeiro operando não definir esse operador, o método para o segundo operando será usado.
Década de 2000
A Microsoft adicionou a sobrecarga de operador ao C # em 2001 e ao Visual Basic .NET em 2003.
Scala trata todos os operadores como métodos e, portanto, permite a sobrecarga do operador por proxy.
No Raku , a definição de todos os operadores é delegada a funções lexicais e, portanto, usando as definições de função, os operadores podem ser sobrecarregados ou novos operadores adicionados. Por exemplo, a função definida na fonte Rakudo para incrementar um objeto Date com "+" é:
multi infix:<+>(Date:D $d, Int:D $x) {
Date.new-from-daycount($d.daycount + $x)
}
Como "multi" foi usado, a função é adicionada à lista de candidatos de multidispatch , e "+" só é sobrecarregado para o caso em que as restrições de tipo na assinatura da função são atendidas. Embora a capacidade de sobrecarga inclua + , * , > = , o postfix e o termo i e assim por diante, também permite sobrecarregar vários operadores de chave: " [ x, y ] ", "x [ y ] ", "x { y } "e" x ( y ) ".
O Kotlin oferece suporte à sobrecarga de operador desde sua criação.
Veja também
- Sobrecarga de função
- Polimorfismo (ciência da computação)
- Sub-rotina
- Operador (programação)
- Operadores em C e C ++
- Método mutator
- Indexador (programação)
- Propriedade (programação)