Sobrecarga de operadores (operator overloading) é um dos inúmeros recursos adicionados ao Delphi para compatibilidade com o .NET Framework. No Delphi 2005 penas a personalidade .NET dispõe desse recurso. No Delphi 2006 temos sobrecarga de operadores também para Win32. Veja neste artigo alguns exemplos de uso e dicas de como implementá-lo.

O que é e para que serve?

A sobrecarga de operadores permite que a linguagem seja estendida e que você possa fazer somas, multiplicações, conversões e várias outras operações entre os objetos que você criou. Imagine que você tenha uma classe que faça soma de matrizes, como mostrado na Listagem 1. Observe como o código fica mais legível.

var

 a, b, c, x: TMatriz;
 
 ...
 
 { Sem operator overloading }
 
 x = a.Soma(b.Soma(c));
 
 { Com operator overloading }
 
 x = a + b + c;
Listagem 1. Código mais legível com o uso de sobrecarga de operadores

Para cada operador que se deseja sobrecarregar é necessário criar um método de classe com uma assinatura específica. Veja na Tabela 1 alguns tipos de operadores existentes e como os métodos devem ser declarados.

Tipo Operadores Assinatura Exemplo de uso
Conversão typecast implícito typecast explícito Implicit(b: TBugalho): TAlho Explicit(b: TBugalho): TAlho Alho := bugalho Alho := TAlho(bugalho)
Unário - + Inc Negative(c: TComplexo): TComplexo Positive(c: TComplexo): TComplexo Inc(m: TMatriz): TMatriz complexo1 := -complexo2 complexo1 := +complexo2 Inc(matriz1)
Comparação = > < Equal(c1,c2: TComplexo): boolean GreaterThan(c1,c2: TComplexo): boolean LessThan(c1,c2: TComplexo): boolean If c1 = c2 then If c1 > c2 then If c1 < c2 then
Binário / div + - Divide(c1,c2: TComplexo): TComplexo IntDivide(c1,c2: TComplexo): TComplexo Add(c1,c2: TComplexo): TComplexo Subtract(c1,c2: TComplexo): TComplexo Result:=c1 / c2 Result:=c1 div c2 Result:=c1 + c2 Result:=c1 – c2
Tabela 1. Apenas alguns operadores que podem ser sobrecarregados

Para exemplificar usei a classe TComplexo (para simbolizar um número complexo, aquele que tem uma parte real e outra imaginária), mas qualquer tipo de objeto ou record pode ser colocado nas assinaturas acima. Em Delphi os operadores são declarados por extenso. Os métodos não podem ser chamados diretamente como métodos comuns, somente na forma de operadores, ou seja, você não pode fazer, por exemplo, o seguinte código:

Result := TMinhaClasse.IntDivide(n1, n2);

Para uma lista completa dos operadores que podem ser sobrecarregados consulte no Help o tópico Delphi 2005 Concepts - Operator Overloads (.NET).

Exemplo 1

Vamos criar uma classe chamada “TLutador”, criar alguns lutadores e sobrecarregar os operadores “>” e “<” . Crie uma aplicação Windows Forms, adicione uma unit e digite o código da Listagem 2.

unit ClubeDelphi.Exemplos.OperatorOverloading.Lutadores;

 

    interface
    
     
    
    type
    
      TLutador = class
    
      private
    
        FForca, FInteligencia, FResistencia: integer;
    
      protected
    
        function Pontuacao: integer;
    
      public
    
        property Forca: integer read FForca write FForca;
    
        property Inteligencia: integer read FInteligencia
    
          write FInteligencia;
    
        property Resistencia: integer read FResistencia
    
          write FResistencia;
    
     
    
        class operator GreaterThan(
    
          const a, b: TLutador): boolean;
    
        class operator LessThan(a, b: TLutador): boolean;
    
      end;
    
     
    
    implementation
    
     
    
    class operator TLutador.GreaterThan(
    
      const a, b: TLutador): boolean;
    
    begin
    
      Result := a.Pontuacao > b.Pontuacao;
    
    end;
    
     
    
    class operator TLutador.LessThan(
    
      a, b: TLutador): boolean;
    
    begin
    
      Result := a.Pontuacao < b.Pontuacao;
    
    end;
    
     
    
    function TLutador.Pontuacao: integer;
    
    begin
    
      Result := FForca*3 + FInteligencia + FResistencia*2;
    
    end;
    
    end.
Listagem 2. Unit Lutadores

No formulário principal adicione um botão e no evento Click adicione o código da Listagem 3.

procedure TWinForm1.Button1_Click(

        sender: System.Object; e: System.EventArgs);
      
      var
      
        Ryu,Ken: TLutador;
      
      begin
      
        { Declare Lutadores na uses }
      
        Ryu := TLutador.Create;
      
        Ryu.Forca := 9;
      
        Ryu.Inteligencia := 8;
      
        Ryu.Resistencia := 8;
      
       
      
        Ken := TLutador.Create;
      
        Ken.Forca := 9;
      
        Ken.Inteligencia := 9;
      
        Ken.Resistencia := 7;
      
        { Na linha abaixo o método GreaterThan
      
          está sendo invocado }
      
        if Ken > Ryu then
      
          MessageBox.Show('Ken!')
      
        { Aqui LessThan está sendo invocado }
      
        else if Ken < Ryu then  
      
          MessageBox.Show('Ryu!')
      
        else
      
          MessageBox.Show('Empate!');
      
      end;
Listagem 3. Confrontando Ken e Ryu, dois ícones do jogo Street Fighter

Exemplo 2

Vamos criar duas classes de matrizes, uma com sobrecarga de operadores e outra sem. Para simplificar, nossa matriz terá dimensões 2 x 2 e seu construtor a preencherá automaticamente. As duas classes serão descendentes de TMatriz. Crie outra unit e digite o código da Listagem 4.

unit ClubeDelphi.Exemplos.OperatorOverloading.Matrizes;

 

        interface
        
         
        
        type
        
          TMatriz = class
        
          public
        
            Numeros: array[1..2, 1..2] of integer;
        
            constructor Create(n: integer = 0);
        
          end;
        
         
        
          TMatrizAntiga = class(TMatriz)
        
            function Soma(m:TMatrizAntiga): TMatrizAntiga;
        
            function FormataString: string;
        
          end;
        
         
        
          TMatrizNova = class(TMatriz)
        
          public
        
            class operator Add(
        
              m1, m2: TMatrizNova): TMatrizNova;
        
            class operator Implicit(m: TMatrizNova): string;
        
          end;
        
         
        
        implementation
        
         
        
        { TMatriz }
        
        constructor TMatriz.Create(n: integer);
        
        begin
        
          inherited Create;
        
          Numeros[1,1] := n;
        
          Numeros[1,2] := n;
        
          Numeros[2,1] := n;
        
          Numeros[2,2] := n;
        
        end;
        
         
        
        {TMatrizAntiga}
        
        function TMatrizAntiga.FormataString: string;
        
        begin
        
          Result := Numeros[1,1].ToString + ' ' +
        
            Numeros[1,2].ToString + #13 +
        
            Numeros[2,1].ToString + ' ' +
        
            Numeros[2,2].ToString;
        
        end;
        
         
        
        function TMatrizAntiga.Soma(
        
          m: TMatrizAntiga): TMatrizAntiga;
        
        var
        
          l, c: integer;
        
        begin
        
          Result := TMatrizAntiga.Create;
        
          for l := 1 to 2 do
        
            for c := 1 to 2 do
        
              Result.Numeros[l, c] :=
        
                Numeros[l, c] + m.Numeros[l, c];
        
        end;
        
         
        
        { TMatrizNova }
        
        class operator TMatrizNova.Add(
        
          m1, m2: TMatrizNova): TMatrizNova;
        
        var
        
          l,c: integer;
        
        begin
        
          Result := TMatrizNova.Create;
        
          for l := 1 to 2 do
        
            for c:=1 to 2 do
        
              Result.Numeros[l, c] :=
        
                m1.Numeros[l, c] + m2.Numeros[l, c];
        
        end;
        
         
        
        class operator TMatrizNova.Implicit(
        
          m: TMatrizNova): string;
        
        begin
        
          Result := m.Numeros[1,1].ToString + ' ' +
        
            m.Numeros[1,2].ToString + #13 +
        
            m.Numeros[2,1].ToString + ' ' +
        
            m.Numeros[2,2].ToString;
        
        end;
        
        end.
        
         
Listagem 4. Comparação entre duas classes de matrizes

No formulário principal adicione dois botões e em seus eventos Click adicione o código da Listagem 5.

{ Declare Matrizes na uses }

        procedure TWinForm1.Button2_Click(
        
          sender: System.Object; e: System.EventArgs);
        
        var
        
          m1,m2,m3,temp: TMatrizAntiga;
        
        begin
        
          m1 := TMatrizAntiga.Create(1);
        
          m2 := TMatrizAntiga.Create(2);
        
          m3 := TMatrizAntiga.Create(3);
        
          temp := m1.Soma(m2.Soma(m3));
        
          MessageBox.Show(temp.FormataString);
        
        end;
        
         
        
        procedure TWinForm1.Button3_Click(
        
          sender: System.Object; e: System.EventArgs);
        
        var
        
          m1,m2,m3: TMatrizNova;
        
        begin
        
          m1 := TMatrizNova.Create(1);
        
          m2 := TMatrizNova.Create(2);
        
          m3 := TMatrizNova.Create(3);
        
          { Aqui o método Add é chamado duas vezes e o método
        
            Implicit chamado uma vez }
        
          MessageBox.Show(m1 + m2 + m3);
        
        end;
        
         
        
        
         
Listagem 5. Testando o código

Ambas as soluções exibem o mesmo resultado. Veja que efetuamos duas somas e uma conversão implícita para string, tudo em apenas uma linha e sem comprometer a legibilidade do código.

Cuidados a serem tomados

  • Não altere comportamentos default para evitar confusão. Numa expressão binária, como por exemplo, a = b + c, os objetos b e c não devem ter seus valores alterados
  • Implemente a sobrecarga de operadores se essa vai ajudar ou simplificar o uso da classe. Se o uso não é intuitivo, a sobrecarga pode ter sido mal aplicada.

Conclusões

Tudo que se faz com operator overload pode-se fazer com chamadas a métodos ou funções. Usuários das versões anteriores do Delphi (6 e 7) podem obter um efeito semelhante usando Custom Variants. Para mais informações dê uma olhada nos links fornecidos.

Tutorial sobre operator overloading usando Free Pascal Documentação da Microsoft sobre operator overloading