Sobrecarga do operador - Operator overloading

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, be cde 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 Timeem 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 tempobjeto é o valor retornado.

A operação também pode ser definida como um método de classe, substituindo lhspelo thisargumento 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 << bababab

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

Referências