Para facilitar a leitura e escrita de código, a sobrecarga de algumas operações é permitida. O operador de sobrecarga é escrito usando a palavra-chave operator. Os seguintes operadores podem ser sobrecarregados:
binário +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
unário +,-,++,--,!,~
operador de atribuição =
operador de indexação []
Sobrecarga de operação permite o uso da notação de operação (escrita na forma de expressões simples) para objetos complexos - estruturas e classes. Escrevendo expressões usando operações de sobrecarga simplifica a visualização do código fonte, porque uma implementação mais complexa fica escondida.
Por exemplo, considere números complexos, que consistem de partes real e imaginária. Eles são amplamente utilizados na matemática. A linguagem MQL5 não tem um tipo de dado que represente números complexos, mas é possível criar um novo tipo de dado na forma de uma estrutura ou classe. Declare a estrutura complexa e defina quatro métodos que implementam as quatro operações aritméticas:
//+------------------------------------------------------------------+
//| Uma estrutura para operações com números complexos |
//+------------------------------------------------------------------+
struct complex
{
double re; // Parte real
double im; // Parte imaginário
//--- Construtores
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Operações Aritméticas
complex Add(const complex &l,const complex &r) const; // Adição
complex Sub(const complex &l,const complex &r) const; // Subtração
complex Mul(const complex &l,const complex &r) const; // Multiplicação
complex Div(const complex &l,const complex &r) const; // Divisão
};
Agora, em nosso código nós podemos declarar variáveis representando números complexos, e trabalhar com eles.
Por exemplo:
void OnStart()
{
//--- Declara e inicialize variáveis de um tipo complexo
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Soma dois números
complex z;
z=a.Add(a,b);
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiplica dois números
z=a.Mul(a,b);
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Dividir dois números
z=a.Div(a,b);
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
Mas seria mais conveniente usar os operadores usuais "+", "-", "*" e "/" para operações aritméticas comuns com números complexos.
A palavra-chave operator é usado para definir uma função membro que realiza conversão de tipo. Operações unárias e binárias para variáveis de objeto de classe podem ser sobrecarregadas como funções membro não estáticas. Elas implicitamente agem nos objetos de classe.
A maioria das operações binárias podem ser sobrecarregadas como funções regulares que tomam uma variável de classe e/ou um ponteiro de objeto desta classe como argumento. Para o nosso tipo complexo, a sobrecarga na declaração se parecerá como:
//--- Operadores
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
O exemplo completo do script:
//+------------------------------------------------------------------+
//| Programa Script da função start (iniciar) |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Declara e inicialize variáveis de um tipo complexo
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//a.re=5;
//a.im=1;
//b.re=-1;
//b.im=-5;
//--- Soma dois números
complex z=a+b;
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiplica dois números
z=a*b;
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Dividir dois números
z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
//+------------------------------------------------------------------+
//| Uma estrutura para operações com números complexos |
//+------------------------------------------------------------------+
struct complex
{
double re; // Parte real
double im; // Parte imaginário
//--- Construtores
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Operações Aritméticas
complex Add(const complex &l,const complex &r) const; // Adição
complex Sub(const complex &l,const complex &r) const; // Subtração
complex Mul(const complex &l,const complex &r) const; // Multiplicação
complex Div(const complex &l,const complex &r) const; // Divisão
//--- Operadores binárias
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
};
//+------------------------------------------------------------------+
//| Adição |
//+------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re+r.re;
res.im=l.im+r.im;
//--- Resultado
return res;
}
//+------------------------------------------------------------------+
//| Subtração |
//+------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re-r.re;
res.im=l.im-r.im;
//--- Resultado
return res;
}
//+------------------------------------------------------------------+
//| Multiplicação |
//+------------------------------------------------------------------+
complex complex::Mul(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re*r.re-l.im*r.im;
res.im=l.re*r.im+l.im*r.re;
//--- Resultado
return res;
}
//+------------------------------------------------------------------+
//| Divisão |
//+------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
{
//--- Numero complexo vazio
complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- Verificar se é zero
if(r.re==0 && r.im==0)
{
Print(__FUNCTION__+": número é zero");
return(res);
}
//--- Variáveis auxiliares
double e;
double f;
//--- Selecionando a variante de cálculo
if(MathAbs(r.im)<MathAbs(r.re))
{
e = r.im/r.re;
f = r.re+r.im*e;
res.re=(l.re+l.im*e)/f;
res.im=(l.im-l.re*e)/f;
}
else
{
e = r.re/r.im;
f = r.im+r.re*e;
res.re=(l.im+l.re*e)/f;
res.im=(-l.re+l.im*e)/f;
}
//--- Resultado
return res;
}
A maioria das operações unárias para classes podem ser sobrecarregadas como funções comuns que aceitam um único argumento de objeto de classe ou ponteiro dele. Adicione sobrecarga de operações unárias "-" e "!".
//+------------------------------------------------------------------+
//| Uma estrutura para operações com números complexos |
//+------------------------------------------------------------------+
struct complex
{
double re; // Parte real
double im; // Parte imaginário
...
//--- Operadores unários
complex operator-() const; // Unary minus
bool operator!() const; // Negação
};
...
//+------------------------------------------------------------------+
//| Sobrecarregar operador de "menos unário" |
//+------------------------------------------------------------------+
complex complex::operator-() const
{
complex res;
//---
res.re=-re;
res.im=-im;
//--- Resultado
return res;
}
//+------------------------------------------------------------------+
//| Sobrecarregar operador de "negação lógica" |
//+------------------------------------------------------------------+
bool complex::operator!() const
{
//--- São as partes real e imaginária do número complexo igual a zero?
return (re!=0 && im!=0);
}
Agora nós podemos verificar se valor de um número complexo é zero e obter um valor negativo:
//+------------------------------------------------------------------+
//| Programa Script da função start (iniciar) |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Declara e inicialize variáveis de um tipo complexo
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Dividir dois números
complex z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- Um número complexo é igual a zero por padrão (no construtor padrão re==0 e im==0
complex zero;
Print("!zero=",!zero);
//--- Atribuir um valor negativo
zero=-z;
PrintFormat("z=%.2f+i*%.2f, zero=%.2f+i*%.2f",z.re,z.im, zero.re,zero.im);
PrintFormat("-zero=%.2f+i*%.2f",-zero.re,-zero.im);
//--- Verificar se é zero mais uma vez
Print("!zero=",!zero);
//---
}
Note que nós não tivemos que sobrecarregar o operador de atribuição "=", já que estruturas de tipos simples pode ser diretamente copiadas uma no outra. Assim, nós agora podemos escrever um código para cálculos envolvendo números complexos de maneira usual.
Sobrecarga de operador de indexação permite obter os valores dos arrays fechados em um objeto, de uma maneira simples e familiar, e isso também contribui para uma melhor legibilidade do código fonte. Por exemplo, nós precisamos fornecer acesso a um símbolo dentro de uma string em uma posição específica. Uma string em MQL5 é um tipo string separado, que não é um array de símbolos, mas com a ajuda de uma operação de indexação sobrecarregada podemos fornecer um trabalho simples e transparente na classe CString gerada: