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;
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 |
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.
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;
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.
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;
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