preview
Do básico ao intermediário: Sobrecarga de operadores (I)

Do básico ao intermediário: Sobrecarga de operadores (I)

MetaTrader 5Exemplos |
77 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Filas, Listas e Árvores (VIII), foi demonstrado o que seria a minha proposta para um entre tantos algoritmos de balanceamento de árvore que podem ser implementados. Como foi dito naquele artigo, cada algoritmo de balanceamento que existe, tem como objetivo permitir tornar a árvore mais eficiente no que rege e tange à parte referente a pesquisa dentro da mesma. Sendo que alguns permitem tornar a árvore mais adequada para pesquisas de um certo tipo, enquanto outro algoritmos, a torna mais adequada para pesquisas de outro tipo.

No caso não iremos entrar em detalhes neste momento sobre a parte de se pesquisar na árvore. Isto por que, antes de mostrar este tipo de implementação, quero mostrar e falar sobre um outro tipo de assunto. Que é bem interessante, apesar de ser muito confuso para a grande parte dos programadores iniciantes. Ainda mais quando eles procuram estudar outros tipos de linguagens, que não possuem tal recurso em seu repositório de coisas que podemos fazer.

O assunto que iremos começar a abordar aqui, tem como objetivo, simplificar de diversas forma a implementação dos códigos futuros. Isto por que, conforme o código vai se tornando cada vez mais complexo, ele em muitos casos acaba ficando cada vez menos legível. Tornando assim mais difícil de entender o objetivo que precisa ser alcançado.

Mas diferente do que muitos pensam, o que será visto aqui torna todo o trabalho de codificação muito mais divertido e agradável. Apesar de que você precisa tomar certos cuidados ao implementar o que será visto aqui. Com a abordagem que estou visando fazer, tende a ser a mais simples quando for possível ser feita. Muito do que irei explicar aqui, deriva justamente da minha experiência em programação em C/C++. No entanto, vou tentar manter as coisas em um nível que seja o mais fácil possível de ser compreendido.

Assim sendo, vamos iniciar o tópico principal do artigo.


Sobrecarga de operadores (I)

Muitas vezes somos levados a programar algo, que acaba não fazendo muito sentido no primeiro momento. Porém, conforme vamos implementado o código e desenvolvendo algumas funções e procedimentos, acabamos nos deparando com um tipo de situação, um tanto quanto complicada de ser resolvida à primeira vista. O problema de fato, não é a dificuldade em se implementar o código propriamente dito. O problema é o deixar o mais simples possível de ser compreendido. Não somente por quem esteja de fato implementando o código. Mas também para quem pretende estudar o mesmo a fim de o melhorar, ou quem sabe usar parte do que esteja sendo implementado ali.

O grande problema na maioria das implementações que precisamos efetuar, se encontra na grande quantidade de funções e procedimentos envolvidos. E o problema real, não é bem o fato de usarmos funções ou procedimentos. Mas sim, o fato de que em muitos casos, tais funções e procedimentos podem ser substituídos por um outro tipo de código. Porém sem o devido conhecimento, fica muito difícil compreender como aplicar esta substituição de maneira correta. O que acaba nos forçando a utilizar um tipo de implementação, que ao logo do tempo, acaba tornando, o que a princípio seria simples, em algo extremamente confuso e disperso. Dificultando de maneira em demasia o correto entendimento do código.

Se você vem acompanhando meus artigos, deve ter notado que evito fazer uso de certos tipos de recursos. Isto para não precisar ficar explicando como os mesmos funcionam. No entanto, devido a esta série de artigos ter como objetivo, passar um outro tipo de conhecimento. Não vejo motivo de ficar apenas utilizando recursos simples. Podemos nos aprofundar em certos recursos, um pouco mais avançados. E ao fazer isto, poderei no futuro implementar os códigos de uma forma, que ao meu entender, é mais simples e fácil de analisar o que estará sendo implementado. E isto sem precisar ficar explicado cada detalhe que esteja sendo implementado. Visto que iremos ver tais detalhes, justamente nestes artigos daqui.

Ok, apesar de saber que boa parte de vocês, podem vir a estar de fato interessados em simplesmente criar algum tipo de indicador, ou expert advisor. Não fazendo muito sentido aprender o que será explicado aqui. Sei que existem outros, que poderão vir a fazer um bom uso do que iremos começar a explorar. Na verdade, ao meu ver, um bom programador terá em sua caixa de ferramentas, coisas nas quais ele quase não irá usar. Porém, toda via e, entretanto, aquele conhecimento, adquirido, justamente para se poder entender por que as coisas são assim e não daquela outra maneira. Pode e fará grande diferença, em momentos mais complicados. Sendo assim, entender de maneira adequada como a sobrecarga de operadores funciona, pode e irá lhe ajudar a tornar muitos de seus códigos, algo bem mais simples de entender e de ser implementado. E entendendo de maneira mais simples um código, fica mais rápido corrigir o mesmo, além de se tornar muito mais fácil e eficiente implementar novas melhorias em algum sistema que você esteja projetando.

Para começar, e isto de maneira correta. Precisamos entender o seguinte: A sobrecarga de operadores, muitas vezes já vem implementada na própria linguagem de programação. E você muitas das vezes, faz uso da mesma, sem de fato compreender que está utilizando a sobrecarga de operadores. Não está acreditando? Bem, então vamos ver um exemplo muito simples, onde a sobrecarga estará sendo utilizada. Este pode ser visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     string sz1;
07. 
08.     sz1 = "Hello world.";
09.     sz1 = sz1 + "How has your day been ?";
10. 
11.     Print(sz1);
12. }
13. //+------------------------------------------------------------------+

Código 01

Acredito que nesta altura do campeonato, todos aqui, já sabem que tipo de coisa irá ser impressa, por este código 01, no terminal do MetaTrader 5. Sendo assim desnecessário mostra o resultado da execução deste código. Porém isto não muda o fato de que aqui, no código 01, mesmo que você não venha a perceber, está de fato usando a sobrecarga de operadores. E isto está sendo feito na linha nove, ao utilizarmos o sinal de soma em um tipo string. Mas espere um pouco. Isto não é uma sobrecarga de operador. Já que estou dizendo ao compilador para pegar o conteúdo da string e adicionar a ele um novo conteúdo. Criando assim um texto com um pouco mais de informação. Então, não vejo isto como sendo uma sobrecarga de operador. Se bem que ainda não sei, o que de fato seria isto. Estou apenas me baseando no que foi explicado quando estudei o artigo que falava sobre a sobrecarga de funções e procedimentos.

Pois bem, meu caro leitor, por mais estranho que possa parecer, aqui no MQL5, assim como outras linguagens também o fazem. Temos a sobrecarga de alguns operadores sendo feita, ou implementada pela própria linguagem. E tudo isto de maneira que você não sinta dificuldades em fazer certos tipos de operações. Tornando assim o código mais legível e fácil de compreender. O fato de isto está sendo feito, nativamente na linguagem, permite que esta linha nove, vista no código 01, trabalhe de maneira, a substituir a chamada de uma função ou procedimento, com objetivo de efetuar uma concatenação dos valores. Porém o operador de soma, a princípio, não tem como objetivo este tipo de coisa. Ele de fato, tem como objetivo, indicar a soma de dois valores, gerando assim um terceiro valor. No entanto, devido justamente a sobrecarga de operadores que a linguagem está fazendo de maneira nativa, permite que o compilador saiba como utilizar o operador de soma, a fim de gerar o que seria a união de informações.

Basicamente este código 01, poderia ser escrito e teria o mesmo tipo de resultado, se fosse criado de uma outra maneira. Onde não faríamos uso da sobrecarga de operadores. Como pode ser visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     string sz1;
07. 
08.     sz1 = "Hello world.";
09.     StringConcatenate(sz1, sz1, "How has your day been ?");
10.     Print(sz1);
11. }
12. //+------------------------------------------------------------------+

Código 02

Note que neste código 02, estamos gerando o mesmo tipo de resultado que será gerado pelo código 01. Sendo que a única diferença entre ambos os códigos é justamente a linha nove. Agora olhe para ambos e responda com toda a sinceridade. Qual dos dois códigos é mais simples de entender, o que estará sendo feito? Acredito que grande parte irá responder que é o código 01. E é justamente isto que a sobrecarga de operadores nos permite fazer. Ela nos permite substituir alguma chamada de função ou procedimento, a fim de tornar o código mais legível.

Agora preste atenção ao seguinte fato. Não existe, a princípio, uma regra para se fazer a sobrecarga de operadores. Apenas existe, o que podemos chamar de um acordo entre cavaleiros. Isto a fim de evitar que situações bizarras venham a ser criadas. Como por exemplo, ao ver um sinal de soma, você não espera que seja gerada uma divisão ou mesmo uma subtração de valores. Porém, nada lhe impede de implementar este tipo de coisa. No entanto, isto acabaria por tornar o código extremamente difícil de ser compreendido, mesmo por você, meu caro leitor, que estará implementando as coisas. E no final algo que poderia ser interessante, acaba por ficar totalmente esquecido. Então prudência nunca é demais, quando o assunto é sobrecarga de operadores.

Ok, então quando permitido pela linguagem, podemos criar o que seria a sobrecarga de operadores. Sendo que cada uma permite um número maior ou menor de operadores podendo ser sobrecarregados. No caso do MQL5, os operadores que podem ser sobrecarregados são vistos na imagem logo na sequência.

Imagem 01

É muito importante saber quais operadores podem ou não ser sobrecarregados pela linguagem, isto quando permitido. E o motivo para isto, é que dependendo de como o código venha a ser implementando, pode vir a ser necessário, você buscar no próprio código, o que de fato um operador estará ou não fazendo com os dados.

Muito bem, você pode notar que os operadores estão divididos em pequenos grupos. Isto facilita bastante o trabalho de implementação da sobrecarga. Visto que ela tem uma modelagem muito semelhante em tipos parecidos. Mas basicamente, a sobrecarga é conseguida com uma sintaxe muito simples, que é mostrada logo abaixo.

tipo operator símbolo (lista de parâmetros)

Então preste atenção a esta informação que está sendo mostrada acima, meu caro leitor. Pois esta é justamente a sintaxe que precisaremos utilizar SEMPRE que for necessário, ou desejável criar a sobrecarga de operador. Agora vamos entender o que cada um destes pontos significa. TIPO seria o tipo de dado que estaremos retornando. Em alguns momentos, pode ser que não venhamos a ter nenhum dado sendo retornado. Nestes casos usaremos o tipo void. Mas isto será visto na prática depois. OPERATOR esta seria uma palavra reservada presente na própria linguagem, permitindo assim que o compilador saiba o que precisa ser feito. SÍMBOLO, aqui é onde utilizaremos um entre os símbolos passiveis de serem sobrecarregados.

No caso, você deverá observar a imagem 01, para saber qual símbolo utilizar. Já a lista de parâmetros, tem como o mesmo objetivo, o que já vem sendo explorado desde os primeiros artigos. A única diferença é que dependendo do operador, podemos ter um ou mais parâmetros podendo aparecer aqui. E é neste momento que entender aquela divisão em grupos, nos ajuda a entender a própria lista de parâmetros. Isto por que operadores no grupo binário, necessitam de um único parâmetro, enquanto os que estão no grupo unitário, NÃO necessitam de nenhum parâmetro. Porém os demais operadores, podem usar um número distinto de parâmetros. Mas vamos chegar lá. Uma coisa de cada vez.

Legal, mas como isto se aplica na prática? Bem, meu caro leitor, esta é a parte divertida. Na prática, e de uma forma geral, a sobrecarga de operadores que iremos e poderemos criar, estará basicamente voltada a trabalhar com tipos que nós iremos criar. Neste ponto se torna necessário que você tenha o devido conhecimento e um bom entendimento do conceito de estruturas.

Para quem está chegando agora, já falamos sobre estruturas, você pode começar, vendo o artigo Do básico ao intermediário: Struct (I). Ali começamos a falar sobre o que seria uma estrutura e como poderíamos utilizar as mesmas. Mas irei considerar o fato de que você, já saiba o que seria uma estrutura de dados. Sendo assim, podemos focar na questão da sobrecarga de operadores.

Uma boa forma de começar a entender a sobrecarga de operadores, é primeiramente entendendo o próprio conceito de sobrecarga. Este conceito foi visto no artigo Do básico ao intermediário: Sobrecarga. No entanto, lá falamos apenas de sobrecarga de funções e procedimentos. Porém entender aquilo irá lhe ajudar a entender este próximo passo. Sendo assim vamos começar com um modelo que podemos dizer se o modelo clássico de apresentação do que seria a sobrecarga de operadores. Este é visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06.     double m_r, m_i;
07. //+----------------+
08.     stComplex(): m_r(0), m_i(0) {}
09. //+----------------+
10.     stComplex(double r, double i): m_r(r), m_i(i) {}
11. //+----------------+
12.     stComplex add(const stComplex &arg1, const stComplex &arg2)
13.     {
14.         return stComplex(arg1.m_r + arg2.m_r, arg1.m_i + arg2.m_i);
15.     }
16. //+----------------+
17. };
18. //+------------------------------------------------------------------+
19. void OnStart(void)
20. {
21.     stComplex   a(2, 5),
22.                 b(8, -3),
23.                 c;
24. 
25.     c = c.add(a, b);
26. 
27.     PrintFormat("C = %.02f %c %.02fi", c.m_r, (c.m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
28. }
29. //+------------------------------------------------------------------+

Código 03

Cara este código me parece muito confuso. Tem certeza que isto é mesmo simples, como você disse? Mas é claro que este código é simples meu caro leitor. Se você o está achando confuso, significa que você não tem estudado, ou visto os artigos anteriores. Neste caso, sugiro que você volte e estude os artigos anteriores, antes de começar a tentar entender o que estaremos fazendo aqui. Pois sem entender o básico, isto daqui não fará o mínimo sentido.

Tudo bem, quando este código 03 for executado, iremos ter como resultado o que é visto na imagem logo abaixo.

Imagem 02

Note que não é nada confuso entender as coisas aqui. Porém ainda não estamos fazendo a sobrecarga de operadores. Apenas quero mostrar como tudo funciona de maneira bem estilo passo a passo. Agora preste atenção ao seguinte fato, meu caro leitor. Observe que nesta linha 25, estamos pedindo para que o código, efetue a soma de dois valores e coloque o resultado em uma outra variável. Da forma como está sendo feita, você claramente nota que é um pouco dispendioso que desejarmos adicionar um terceiro valor a este cálculo. Já que se isto fosse feito, precisaríamos, colocar mais chamadas a função add, vista na linha 12. O que no final acabaria tornando o código bem confuso, devido ao número crescente de chamadas sendo feitas. Porém se fizermos uma simples mudança no código, a coisa toda muda completamente de figura. Visto que poderíamos utilizar o operador de soma, para efetuar o mesmo tipo de coisa que está sendo feita pela função implementada na linha 12.

Mas espere um pouco aí, você está me dizendo que podemos utilizar o operador de soma neste código 03? Sim, meu caro leitor, estou dizendo que podemos fazer isto. Porém, toda via e, entretanto, quero que você entenda muito bem o que estou dizendo. Por isto, vamos modificar o código a fim de usar o operador de soma no lugar desta chamada vista na linha 25. Você a princípio pode imaginar que a mudança seria esta que é mostrada logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06.     double m_r, m_i;
07. //+----------------+
08.     stComplex(): m_r(0), m_i(0) {}
09. //+----------------+
10.     stComplex(double r, double i): m_r(r), m_i(i) {}
11. //+----------------+
12.     stComplex add(const stComplex &arg1, const stComplex &arg2)
13.     {
14.         return stComplex(arg1.m_r + arg2.m_r, arg1.m_i + arg2.m_i);
15.     }
16. //+----------------+
17. };
18. //+------------------------------------------------------------------+
19. void OnStart(void)
20. {
21.     stComplex   a(2, 5),
22.                 b(8, -3),
23.                 c;
24. 
25.     c = a + b;
26. 
27.     PrintFormat("C = %.02f %c %.02fi", c.m_r, (c.m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
28. }
29. //+------------------------------------------------------------------+

Código 04

Auto lá. Este código 04 funciona? Não meu caro leitor, se você tentar compilar este código 04, irá receber os seguintes erros do compilador.

Imagem 03

Isto por que, o compilador NÃO SABE como somar dois números complexos. Ou melhor dizendo, ele, o compilador, não sabe como trabalhar com um tipo de dado que foi criado por um usuário, ou programador. Porém ainda assim, podemos criar fazer algo diferente, lembrando que cada caso é um caso. E neste caso, poderíamos utilizar o código visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06.     double m_r, m_i;
07. //+----------------+
08.     stComplex(): m_r(0), m_i(0) {}
09. //+----------------+
10.     stComplex(double r, double i): m_r(r), m_i(i) {}
11. //+----------------+
12.     stComplex add(const stComplex &arg1, const stComplex &arg2)
13.     {
14.         return stComplex(arg1.m_r + arg2.m_r, arg1.m_i + arg2.m_i);
15.     }
16. //+----------------+
17. };
18. //+------------------------------------------------------------------+
19. void OnStart(void)
20. {
21.     stComplex   a(2, 5),
22.                 b(8, -3),
23.                 c;
24. 
25.     c = stComplex(a.m_r + b.m_r, a.m_i + b.m_i);
26. 
27.     PrintFormat("C = %.02f %c %.02fi", c.m_r, (c.m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
28. }
29. //+------------------------------------------------------------------+

Código 05

Sei que esta linha 25, vista neste código 05, pode lhe parecer bastante estranha. Mas ela de fato funciona. Isto por que, estamos utilizando o constructor da linha 10, a fim de criar o tipo de dado complexo. E ao fazermos isto, o compilador irá conseguir entender como trabalhar com um tipo de dado, que a priori ele não sabia como operar. Estou mostrando isto, para lhe chamar a atenção a um outro tipo de operação, que muitos consideram ser algo relativamente simples. Mas dependendo do caso, pode ser algo bem complicado. Que é justamente o fato de estarmos utilizando o operador de atribuição para configurar um valor a uma determinada variável. Normalmente, somente podemos atribuir valores de um tipo a uma variável de um mesmo tipo. Porém existem situações, em que deveremos atribuir um tipo de valor a uma variável de tipo completamente diferente. E quando isto for necessário ser feito, precisaremos sobrecarregar este operador de atribuição, caso contrário, não conseguiremos fazer o código funcionar.

Mas vamos voltar a nossa questão inicial, que seria justamente implementar a nossa primeira sobrecarga de operador. Como já vimos, podemos fazer as coisas de diferentes maneiras. No entanto, não conseguimos ainda fazer com que o código 04 se tornar funcional. Agora pergunto: Como poderíamos fazer isto? Bem, a resposta está no começa do artigo. Mas como assim? Não entendi. Muito bem, lembra de que eu mencionei o fato de que existe uma sintaxe para se criar a sobrecarga de um operador?

Pois bem, se temos uma função ou procedimento, que a princípio já faz o tipo de fatoração que precisamos. Podemos fazer com que esta função ou procedimento se torne um operador sobrecarregado. Tipo o que está ocorrendo na linha 12 vista deste o código 03. Ali temos uma função cujo trabalho é o de efetuar exatamente a soma de dois valores complexos, a fim de nos retornar um valor resultante. Então se modificarmos o nome daquela função para um outro em que o compilador entenda como sendo um operador, iremos conseguir fazer com que o código 04 se torne funcional. Você pode estar olhando e pensando: Mas isto parece bem complicado de ser feito. Como eu poderia dizer ao compilador como fazer isto? Pois bem, esta é a parte fácil. Veja como isto foi feito, comparando o código 04 com este mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06.     double m_r, m_i;
07. //+----------------+
08.     stComplex(): m_r(0), m_i(0) {}
09. //+----------------+
10.     stComplex(double r, double i): m_r(r), m_i(i) {}
11. //+----------------+
12.     stComplex operator+(const stComplex &arg1)
13.     {
14.         return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
15.     }
16. //+----------------+
17. };
18. //+------------------------------------------------------------------+
19. void OnStart(void)
20. {
21.     stComplex   a(2, 5),
22.                 b(8, -3),
23.                 c;
24. 
25.     c = a + b;
26. 
27.     PrintFormat("C = %.02f %c %.02fi", c.m_r, (c.m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
28. }
29. //+------------------------------------------------------------------+

Código 06

Não entendi. Este código 06 está de fato, muito parecido com o código 04. Porém observando com calma a linha 12, percebo que estamos usando um número diferente de argumentos. Por que? Bem, o problema é que você está olhando a linha 25 vendo ali um operador de soma sendo utilizado. Porém, quando usamos a sobrecarga de operadores, você deve olhar esta linha 25 de uma outra maneira. E é aqui, onde muitos iniciantes ficam extremamente perdidos, quando se deparam com um código que está fazendo uso da sobrecarga de operador. Isto por que, para o iniciante, este operador de soma, não é de fato uma chamada a uma função. Ele é somente um símbolo presente no código. Porém, e é isto que você meu estimado leitor, precisa compreender. Ao ver esta linha 25, o compilador não estará vendo o código como é mostrado no código 06. Ele na verdade estará vendo algo parecido com o que é mostrado no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06.     double m_r, m_i;
07. //+----------------+
08.     stComplex(): m_r(0), m_i(0) {}
09. //+----------------+
10.     stComplex(double r, double i): m_r(r), m_i(i) {}
11. //+----------------+
12.     stComplex operator+(const stComplex &arg1)
13.     {
14.         return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
15.     }
16. //+----------------+
17. };
18. //+------------------------------------------------------------------+
19. void OnStart(void)
20. {
21.     stComplex   a(2, 5),
22.                 b(8, -3),
23.                 c;
24. 
25.     c = a.operator+(b);
26. 
27.     PrintFormat("C = %.02f %c %.02fi", c.m_r, (c.m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
28. }
29. //+------------------------------------------------------------------+

Código 07

Ambos os códigos, tanto o 06 quanto o 07, são a mesma coisa. Porém é mais fácil encontrar alguém codificando as coisas, como é visto no código 06. No entanto, você, como programador, deve pensar e entender o código da mesma forma que o compilador o estará vendo. E para isto, você precisa ver o código, como sendo este código 07. Onde na linha 25, temos o que seria a chamada para a função da linha 12. Sei que isto, neste começo de apresentação parece muito, mas muito mais confuso do que realmente é. Porém, ao olhar este código 07, você consegue finalmente entender por que, na linha 12, precisamos de apenas e somente um único argumento sendo repassado para a função. Coisa que de outra maneira, você jamais conseguiria entender se fosse explicado de outra forma.

Hum, legal. Agora sim gostei realmente de aprender uma coisa da qual eu não sabia. Esta coisa de sobrecarga de operador parece ser algo bem legal mesmo. Mas vem cá, será que somente podemos fazer as coisas assim? Será que não poderíamos fazer por exemplo a soma de um número complexo, com um inteiro por exemplo?

Bem, esta é uma coisa um pouco mais complicada de ser entendida. Porém a resposta para isto é SIM. Podemos somar um inteiro, com este número complexo que estamos definindo dentro da estrutura. Mas para tornar as coisas um pouco mais simples. Já que isto é algo um tanto quanto complicado de entender, assim logo de cara. Vamos mudar uma coisa nesta estrutura stComplex. Para começar, você pode notar que estamos permitindo que as variáveis internas sejam vistas fora da estrutura. O que de certa forma não é uma boa ideia. Assim, vamos mudar o código, de forma que tornaremos estas mesmas variáveis ocultas dentro da estrutura. Uma vez feito isto, podemos pensar em como resolver esta questão de somar um valor inteiro, com um valor presente na estrutura.

Desta forma a primeira parte da modificação é vista logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06. //+----------------+
07.     private :
08.         double m_r, m_i;
09. //+----------------+
10.     public  :
11. //+----------------+
12.         stComplex(): m_r(0), m_i(0) {}
13. //+----------------+
14.         stComplex(double r, double i): m_r(r), m_i(i) {}
15. //+----------------+
16.         stComplex operator+(const stComplex &arg1)
17.         {
18.             return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
19.         }
20. //+----------------+
21.         void Debug(void)
22.         {
23.             PrintFormat("Internal Value = %.02f %c %.02fi", m_r, (m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
24.         }
25. //+----------------+
26. };
27. //+------------------------------------------------------------------+
28. void OnStart(void)
29. {
30.     stComplex   a(2, 5),
31.                 b(8, -3),
32.                 c;
33. 
34.     c = a + b;
35.     c.Debug();
36. }
37. //+------------------------------------------------------------------+

Código 08

Ok, agora vamos ao que você queria saber se era possível de ser feito. Bem para isto, primeiro preciso que você saiba como fazer a sobrecarga de funções e procedimentos. Eu mencionei que isto seria necessário, no começo do artigo, e qual artigo tem tais informações. Uma vez que você saiba, como efetuar tal sobrecarga, podemos pensar em como somar um valor inteiro a um valor criado e mantido pela estrutura stComplex. Assim tudo que precisamos fazer é criar um procedimento como mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06. //+----------------+
07.     private :
08.         double m_r, m_i;
09. //+----------------+
10.     public  :
11. //+----------------+
12.         stComplex(): m_r(0), m_i(0) {}
13. //+----------------+
14.         stComplex(double r, double i): m_r(r), m_i(i) {}
15. //+----------------+
16.         stComplex operator+(const stComplex &arg1)
17.         {
18.             return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
19.         }
20. //+----------------+
21.         stComplex add(const double arg2)
22.         {
23.             return stComplex(m_r + arg2, m_i);
24.         }
25. //+----------------+
26.         void Debug(void)
27.         {
28.             PrintFormat("Internal Value = %.02f %c %.02fi", m_r, (m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
29.         }
30. //+----------------+
31. };
32. //+------------------------------------------------------------------+
33. void OnStart(void)
34. {
35.     stComplex   a(2, 5),
36.                 b(8, -3),
37.                 c;
38. 
39.     c = a + b;
40.     c.Debug();
41. 
42.     c = c.add(4);
43.     c.Debug();
44. }
45. //+------------------------------------------------------------------+

Código 09

Quando este código 09 for executado, você terá como resultado o que é visto na imagem logo na sequência.

Imagem 04

Mostrando nitidamente que sim, podemos somar um valor inteiro a um valor presente dentro da estrutura stComplex. Porém existe uma questão aqui, meu caro leitor. E esta é justamente a parte complicada. Por conta disto, é que ainda não fizemos a implementação da sobrecarga de operador diretamente neste código 09. Primeiro preciso que você entenda uma coisa. O que quero que você entenda é que a ordem dos fatores irá influenciar no fato de o código poder ou não ser compilado.

Perceba o seguinte: Quando o compilador for interpretar o código, isto a fim de gerar o executável. Ele precisará ver algo como o que foi mostrado no código 07. Ou seja, dependendo da forma como a linha de código for escrita, o compilador NÃO IRÁ GERAR um executável. Isto por que ele não está conseguindo entender o que estamos tentando fazer. Por isto entender a sobrecarga de funções e procedimentos é importante para se conseguir entender como utilizar de maneira mais ampla a sobrecarga de operadores. Você pode até achar que uma coisa nada tem a ver com a outra. Porém está tudo interligado. Portanto sem entender algo que a priori não fazia muito sentido lá no começo. Agora passa a fazer todo o sentido de ser devidamente compreendido todos aqueles conceitos.

Agora preste atenção, quando fizemos a mudança do código 03 para o código 06. Reduzimos um argumento na chamada. Agora para resolver esta questão vista no código 09, precisamos pensar um pouco mais além. Ou seja, temos que ver as coisas fora da caixa. Isto por que, existem dois tipos de casos em que podemos efetuar algo similar ao visto no código 09. O primeiro caso, é o que o próprio código 09 está abordando. Já o segundo caso é o que podemos ver no código mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06. //+----------------+
07.     private :
08.         double m_r, m_i;
09. //+----------------+
10.     public  :
11. //+----------------+
12.         stComplex(): m_r(0), m_i(0) {}
13. //+----------------+
14.         stComplex(double r, double i): m_r(r), m_i(i) {}
15. //+----------------+
16.         stComplex operator+(const stComplex &arg1)
17.         {
18.             return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
19.         }
20. //+----------------+
21.         stComplex add(const double arg2)
22.         {
23.             return stComplex(m_r + arg2, m_i);
24.         }
25. //+----------------+
26.         void Debug(void)
27.         {
28.             PrintFormat("Internal Value = %.02f %c %.02fi", m_r, (m_i < 0 ? '-' : '+'), MathAbs(c.m_i));
29.         }
30. //+----------------+
31. };
32. //+------------------------------------------------------------------+
33. void OnStart(void)
34. {
35.     stComplex   a(2, 5),
36.                 b(8, -3),
37.                 c;
38. 
39.     c = a + b;
40.     c.Debug();
41. 
42.     c = b.add(4);
43.     c.Debug();
44. }
45. //+------------------------------------------------------------------+

Código 10

De fato, ao executar este código 10, será gerado o que pode ser visto na imagem logo na sequência.

Imagem 05

Note que no caso do código 09, estávamos somando um valor com uma variável. Já no caso do código 10, estamos somando o valor inteiro com uma outra variável. Devido a isto, temos dois tipos de situação que precisa ser coberta, caso venhamos a utilizar a sobrecarga de operadores. E é por conta disto que falei que este tipo de coisa é um pouco mais complicado e confusa. Mas nada que não pode ser mostrado e explicado aqui.

Ok, então temos duas situações diferentes, necessitando ser tratadas. Como podemos resolver isto da maneira o mais simples possível? Bem, a maneira mais simples de se resolver isto é usando algo parecido com o código 11, que é mostrado na íntegra logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct stComplex
05. {
06. //+----------------+
07.     private :
08.         double m_r, m_i;
09. //+----------------+
10.     public  :
11. //+----------------+
12.         stComplex(): m_r(0), m_i(0) {}
13. //+----------------+
14.         stComplex(double r, double i): m_r(r), m_i(i) {}
15. //+----------------+
16.         stComplex operator+(const stComplex &arg1)
17.         {
18.             return stComplex(m_r + arg1.m_r, m_i + arg1.m_i);
19.         }
20. //+----------------+
21.         stComplex operator+(const double arg2)
22.         {
23.             return stComplex(m_r + arg2, m_i);
24.         }
25. //+----------------+
26.         stComplex operator+=(const double arg2)
27.         {
28.             return stComplex(m_r += arg2, m_i);
29.         }
30. //+----------------+
31.         void Debug(void)
32.         {
33.             PrintFormat("Internal Value = %.02f %c %.02fi", m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i));
34.         }
35. //+----------------+
36. };
37. //+------------------------------------------------------------------+
38. void OnStart(void)
39. {
40.     stComplex   a(2, 5),
41.                 b(8, -3),
42.                 c;
43. 
44.     c = a + b;
45.     c.Debug();
46. 
47.     (c += 4).Debug();
48.     c = b + 4;
49.     c.Debug();
50. }
51. //+------------------------------------------------------------------+

Código 11

Pois bem, aqui eu brinquei um pouco com o código, a fim de mostrar uma outra coisa. Mas de qualquer forma, o foco ainda é a sobrecarga de operadores. Com isto, quando este código 11 for executado, você terá como resultado o que é visto na imagem logo abaixo.

Imagem 06

Perceba que conseguimos cobrir ambos os casos, que foram mostrados nos códigos 09 e 10. E isto de uma maneira bastante simples e divertida. Porém o que foi visto aqui é apenas a base da base do que precisamos para entender o que será visto nos próximos artigos.


Considerações finais

Neste artigo vimos como poderíamos pensar e imaginar a sobrecarga de operadores. Se bem que aqui foi visto apenas a parte mais simples de todo um procedimento que ainda iremos explorar bem mais afundo conforme os artigos vierem a ser escritos. Com o tempo, e com a prática, você irá ver que este recurso, que aqui no MQL5, é muito pouco explorado, pode e irá abrir diversas portas para você, meu caro leitor. Isto por conta do simples fato de que, quando bem empregada, a sobrecarga de operadores, nos permite criar um código que é muito mais legível e simples de entender. E justamente por conta disto, o código se tornar muito mais seguro, estável e fácil de ser melhorado. Além é claro nos permitir um melhor controle sobre o que de fato estamos fazendo.

Contudo, quero lembrar a você, meu caro leitor, de que se você, por ventura veio a cair de paraquedas diretamente neste artigo, e que estejam começando e desejando aprender programação MQL5. Que procure ler e estudar o conteúdo dos artigos que vieram antes deste daqui. Isto por que, cada um daqueles artigos, traz consigo uma pequena parte de um todo. Que você precisa entender para conseguir acompanhar o que estaremos mostrando nos próximos artigos. De qualquer forma, não se esqueça de estudar e praticar o que foi visto neste artigo, fazendo para isto o uso dos arquivos presentes no anexo. E no próximo artigo iremos ver um pouco mais sobre esta coisa chamada sobrecarga de operadores. Pois a diversão está apenas começando.

Arquivo MQ5Descrição
Code 01 Demonstração básica
Code 02 Demonstração básica
Code 03 Demonstração básica
Code 04 Demonstração básica
Code 05  Demonstração básica
Code 06  Demonstração básica
Code 07  Demonstração básica
Code 08  Demonstração básica
Arquivos anexados |
Anexo.zip (3.59 KB)
Simulação de mercado: Position View (XX) Simulação de mercado: Position View (XX)
Neste artigo iremos ver como modificar o código do indicador de posição a fim de conseguir, criar um tipo de sombra para que possamos visualizar onde o preço se encontra atualmente no servidor de negociação. Tal principio tem como finalidade facilitar o planejamento de operações. Onde temos uma movimentação das linhas de stop loss ou take profit. Porém adicionar tal funcionalidade, ou seja sombras de preço. Pode parecer algo extremamente complexo. Mas neste artigo mostrarei que você conseguirá fazer isto de maneira muito simples e prática.
Classes de tabela e cabeçalho baseadas no modelo de tabela em MQL5: Aplicação do conceito MVC Classes de tabela e cabeçalho baseadas no modelo de tabela em MQL5: Aplicação do conceito MVC
Esta é a segunda parte do artigo dedicada à implementação de um modelo de tabela em MQL5, utilizando o paradigma arquitetural MVC (Model-View-Controller). O artigo aborda o desenvolvimento das classes da tabela e de seu cabeçalho, com base no modelo de tabela criado anteriormente. As classes desenvolvidas servirão como base para a futura implementação dos componentes de visualização (View) e controle (Controller), que serão abordados nos próximos artigos.
Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 10): Golden Cross e Death Cross Estratégicos (EA) Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 10): Golden Cross e Death Cross Estratégicos (EA)
Você sabia que as estratégias Golden Cross e Death Cross, baseadas no cruzamento de médias móveis, são alguns dos indicadores mais confiáveis para identificar tendências de mercado de longo prazo? Um Golden Cross sinaliza uma tendência de alta quando uma média móvel mais curta cruza acima de uma média mais longa, enquanto o Death Cross indica uma tendência de baixa quando a média mais curta cruza abaixo. Apesar de sua simplicidade e eficácia, aplicar essas estratégias manualmente frequentemente leva a oportunidades perdidas ou negociações atrasadas.
Integrando Discord com MetaTrader 5: Construindo um Bot de Trading com Notificações em Tempo Real Integrando Discord com MetaTrader 5: Construindo um Bot de Trading com Notificações em Tempo Real
Neste artigo, veremos como integrar o MetaTrader 5 a um servidor Discord para receber notificações de negociações em tempo real de qualquer lugar. Veremos como configurar a plataforma e o Discord para habilitar o envio de alertas ao Discord. Também abordaremos questões de segurança que surgem em conexão com o uso de WebRequests e webhooks para esse tipo de solução de alertas.