Do básico ao intermediário: Sobrecarga de operadores (III)
Introdução
No artigo anterior Do básico ao intermediário: Sobrecarga de operadores (II), foi demonstrado como poderíamos criar uma forma e os perigos envolvido durante o processo de depuração de um código que esteja utilizando sobrecarga de operadores. Apesar de a princípio, aquele artigo parecer um tanto quanto confuso e complicado. O conteúdo ali presente é de fato algo no qual você deve tentar ao máximo compreender, meu caro leitor. Isto por que pode ser que em algum momento, você venha a ter dificuldades em depurar um código que esteja utilizando a sobrecarga de operadores. E sem aquele conhecimento, você não conseguirá lidar de maneira adequada com alguns problemas bem chatos de serem resolvidos.
Muito bem, apesar de já ter apresentado alguns dos operadores que podem ser sobrecarregados aqui no MQL5. Pelo menos dois deles. Acredito que não será difícil para você, conseguir adaptar o que já foi explicado para uma boa parte deles. Pelo menos no que diz respeito aos operadores aritméticos. Porém existem outros tipos de operadores. E a forma de fazermos a sobrecarga dos mesmos é um pouco diferente. Tendo em alguns casos algumas peculiaridades bem específicas.
Ok, dos operadores que ainda falta demonstrar como se trabalhar com eles. Neste artigo iremos tratar dos operadores lógicos, ou como muitos os costuma tratar, operadores relacionais. Então, chegou a hora de se desligar das coisas que podem lhe distrair. E vamos ao que interessa. Mas para isto, iremos iniciar um novo tópico.
Sobrecarga de operadores (III)
Beleza, já que falamos um pouco sobre como trabalhar com os operadores aritméticos. Acredito que você, já deva estar ansioso para aprender como utilizar os operadores que nos permite desviar o fluxo de execução do código, dependendo se uma condição seja verdadeira ou falsa. Normalmente quando vamos fazer isto, precisamos testar diversas variáveis que possivelmente estejam dentro de uma estrutura, ou classe. Porém existe uma forma bem mais prática e simples de se fazer isto. Desde é claro você construa a classe ou estrutura de uma maneira, muito bem pensada. Caso contrário, poderá acabar dando com a cara no muro. Pois diferente dos operadores aritméticos, os operadores lógicos e de relação. Não permitem falhas, e exigem bem mais atenção por parte do programador. Mas se você compreendeu o que foi visto nos dois artigos anteriores. Entender este daqui será bem mais fácil.
Ok, então vamos começar. Mas para que tudo fique mais agradável e também bem palatável. Vamos fazer o seguinte: Não vamos criar um código do zero. Vamos utilizar o que foi visto no artigo anterior. Sendo assim, o código inicial pode ser visto 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 &arg) 17. { 18. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 19. } 20. //+----------------+ 21. stComplex operator+(const double arg) 22. { 23. return stComplex(m_r + arg, m_i); 24. } 25. //+----------------+ 26. stComplex operator+=(const double arg) 27. { 28. return stComplex(m_r += arg, m_i); 29. } 30. //+----------------+ 31. stComplex Debug(uint arg) 32. { 33. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 34. return this; 35. } 36. //+----------------+ 37. }; 38. //+------------------------------------------------------------------+ 39. void OnStart(void) 40. { 41. stComplex a(2, 5), 42. b(8, -3), 43. c; 44. 45. c = (a + b).Debug(__LINE__); 46. c.Debug(__LINE__); 47. (c += 4).Debug(__LINE__); 48. c = (b + 4).Debug(__LINE__); 49. c.Debug(__LINE__); 50. } 51. //+------------------------------------------------------------------+
Código 01
De certa forma, você não precisa trabalhar da maneira como estou fazendo aqui. Na prática, você pode simplesmente implementar o que é necessário. Porém, devido ao fato de querer deixar o artigo, bastante didático. Prefiro usar algo que todos já sabem como funciona. Agora a ideia aqui, será criar uma forma de fazer com que o fragmento logo abaixo, venha a funcionar.
. . . 38. //+------------------------------------------------------------------+ 39. void OnStart(void) 40. { 41. for (stComplex c(1, 0), step(1, 3), max(5, 6); c < max; c += step) 42. c.Debug(__LINE__); 43. } 44. //+------------------------------------------------------------------+
Fragmento 01
Sim eu sei, isto parece muita doideira. Isto pelo fato, de que você nunca viu algo assim sendo produzido aqui no MQL5. Pelo menos não de maneira pública. Porém, isto não significa que não podemos criar um laço for da forma como está sendo mostrado neste fragmento. Por mais estranho que isto possa parecer. Neste momento, muitos de vocês devem estar duvidando da minha sanidade mental. Mas conforme as coisas forem sendo implementadas você irá ver que podemos sim fazer muitas coisas no MQL5. Tudo que precisamos é conhecer os conceitos certos e os aplicar de maneira adequada. Muito bem, então se modificarmos o procedimento OnStart, para este mostrado no fragmento 01. Você irá obter a seguinte resposta do compilador.

Imagem 01
Certo, isto já era esperado. E o motivo é simples. O compilador não sabe como utilizar o operador de menor. Já que estamos utilizando um tipo de dado que o compilador desconhece a forma de trabalhar com ele. Mas e quanto os dois outros erros? Bem, meu caro leitor, estes dois outros erros são na verdade um só. Se você observar o código 01, irá ver que o operador += não está sendo implementado para o tipo stComplex. Mas sim para o tipo double. Você pode ver isto, observando a linha 26 do código 01.
Então vamos começar corrigindo este primeiro erro, que está relacionado ao operador +=. Isto para que ele consiga somar o tipo stComplex, assim como também somar valores double. Para isto, o código será modificado 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 &arg) 17. { 18. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 19. } 20. //+----------------+ 21. stComplex operator+(const double arg) 22. { 23. return stComplex(m_r + arg, m_i); 24. } 25. //+----------------+ 26. stComplex operator+=(const double arg) 27. { 28. return stComplex(m_r += arg, m_i); 29. } 30. //+----------------+ 31. stComplex operator+=(const stComplex &arg) 32. { 33. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 34. } 35. //+----------------+ 36. stComplex Debug(uint arg) 37. { 38. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 39. return this; 40. } 41. //+----------------+ 42. }; 43. //+------------------------------------------------------------------+ 44. void OnStart(void) 45. { 46. for (stComplex c(1, 0), step(1, 3), max(5, 6); c < max; c += step) 47. c.Debug(__LINE__); 48. } 49. //+------------------------------------------------------------------+
Código 02
Agora se tentarmos novamente compilar o código, teremos o compilador reportando a seguinte condição.

Imagem 02
Perfeito. Agora vamos fazer uma coisa, antes de ver este laço da linha 46, neste código 02 funcionar. Isto por que, não é muito prudente, testar uma sobrecarga de operador relaciona, ou um operador lógico, diretamente em um laço. O melhor é utilizar um comando if para efetuar tal teste. Assim iremos modificar levemente o código 02. A fim de produzir este tipo de estudo. Com isto o código 02, irá ficar 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 &arg) 17. { 18. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 19. } 20. //+----------------+ 21. stComplex operator+(const double arg) 22. { 23. return stComplex(m_r + arg, m_i); 24. } 25. //+----------------+ 26. stComplex operator+=(const double arg) 27. { 28. return stComplex(m_r += arg, m_i); 29. } 30. //+----------------+ 31. stComplex operator+=(const stComplex &arg) 32. { 33. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 34. } 35. //+----------------+ 36. bool operator<(const stComplex &arg) 37. { 38. return ((m_r <= arg.m_r) && (m_i < arg.m_i)); 39. } 40. //+----------------+ 41. stComplex Debug(uint arg) 42. { 43. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 44. return this; 45. } 46. //+----------------+ 47. }; 48. //+------------------------------------------------------------------+ 49. void OnStart(void) 50. { 51. stComplex a(1, 3), 52. b(1, 4); 53. 54. if (a < b) Print("Condition is: True"); else Print("Condition is: False."); 55. // for (stComplex c(1, 0), step(1, 3), max(5, 6); c < max; c += step) 56. // c.Debug(__LINE__); 57. } 58. //+------------------------------------------------------------------+
Código 03
Note que comentei as linhas onde o laço for estaria sendo executado, e criei algumas linhas para verificar se o operador < seria de fato implementado de maneira correta. Com isto, quando você executar este código 03, irá ver no terminal do MetaTrader 5, algo parecido com o que é visto logo abaixo.

Imagem 03
Legal, funciona. Então agora já podemos liberar o código referente ao laço for e verificar se ele irá de fato funcionar de maneira correta. Porém, como precaução nunca é demais. Vamos adicionar uma etapa extra no sistema, antes de testar este laço for. Isto por que, pode ser que o laço entre em um loop infinito, já que ainda não temos certeza de como o código irá se comportar com estes operadores sobrecarregados. Desta forma surge o código visto 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 &arg) 17. { 18. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 19. } 20. //+----------------+ 21. stComplex operator+(const double arg) 22. { 23. return stComplex(m_r + arg, m_i); 24. } 25. //+----------------+ 26. stComplex operator+=(const double arg) 27. { 28. return stComplex(m_r += arg, m_i); 29. } 30. //+----------------+ 31. stComplex operator+=(const stComplex &arg) 32. { 33. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 34. } 35. //+----------------+ 36. bool operator<(const stComplex &arg) 37. { 38. return ((m_r <= arg.m_r) && (m_i < arg.m_i)); 39. } 40. //+----------------+ 41. stComplex Debug(uint arg) 42. { 43. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 44. return this; 45. } 46. //+----------------+ 47. }; 48. //+------------------------------------------------------------------+ 49. void OnStart(void) 50. { 51. stComplex a(1, 3), 52. b(1, 3), 53. m(5, 6); 54. int counter = 0; 55. 56. while (a < m) 57. { 58. a.Debug(__LINE__); 59. a += b; 60. counter++; 61. if (counter > 50) break; 62. } 63. 64. a.Debug(__LINE__); 65. Print("The counter value is: ", counter); 66. 67. // for (stComplex c(1, 0), step(1, 3), max(5, 6); c < max; c += step) 68. // c.Debug(__LINE__); 69. } 70. //+------------------------------------------------------------------+
Código 04
Agora veja o que mudou no código. Assim ao executar ele, termos como resultado o que é visto logo abaixo.

Imagem 04
Hum, isto parece que não estar correto. Ou está? Na verdade não meu caro leitor. Por isto, que mencionei o fato de sempre testarmos as coisas com cautela. Pois da mesma forma que a contagem terminou de maneira errada. Também poderíamos ter entrado em um loop infinito. Por isto que operadores lógicos e de relação, precisam de um cuidado bem maior que os operadores aritméticos ao serem sobrecarregados. Se você cometer um pequeno deslise. Pronto, lá se vão horas tentando entender onde está o erro. Por isto, que fico repetindo em todo artigo, que você precisa estudar e praticar o que está sendo mostrado aqui. Isto para conseguir ter noção de quando é hora ou não de usar uma coisa que você acabou de implementar.
Certo. Mas onde está o problema aqui no código? Esta é a parte fácil, meu caro leitor. O problema está na linha 38. Perceba o seguinte: Quando testamos usando o comando if, esta linha 38 permitiu que o teste fosse executado de maneira correta. Porém quando usamos um comando while, o teste já não funcionou de forma correta. Isto por que, o valor m_i faz com que ele se retorna falso. Mesmo antes de o valor m_r não ter sido superado ou alcançado. Viu como é simples entender onde está o erro? Então para corrigir isto, precisamos mudar a linha 38, vista no código 04, por esta vista no fragmento 02 logo abaixo.
. . . 38. return ((m_r < arg.m_r) ? true : (m_r > arg.m_r ? false : (m_i < arg.m_i)));; . . .
Fragmento 02
Executando novamente o código, agora temos o seguinte resultado:

Imagem 05
Perfeito. Funcionou maravilhosamente bem. Agora finalmente podemos liberar o laço for para ser utilizado. Com isto, podermos em fim, fazer uso do que foi visto no fragmento 01. Assim, executando o código, agora via laço for, o resultado é o que podemos ver na imagem abaixo.

Imagem 06
Simples e muito divertido, não é mesmo meu caro leitor? Muito bem, esta foi a parte fácil. Agora vamos tornar as coisas um pouco mais complicadas, e ao mesmo tempo bem mais divertidas. Para tal, vamos implementar o operador de >. Isto é feito adicionando ao código, o que é visto no fragmento logo abaixo.
. . . 40. //+----------------+ 41. bool operator>(const stComplex &arg) 42. { 43. return ((m_r > arg.m_r) ? true : (m_r < arg.m_r ? false : (m_i > arg.m_i))); 44. } 45. //+----------------+ . . .
Fragmento 03
Note que ele é igualmente bem simples, sendo quase o oposto do operador que vimos a pouco sendo implementado. Agora podemos tanto comparar se um valor é maior ou menor. Porém, toda via e, entretanto, existe uma limitação neste tipo de comparação. Assim como alguns cuidados que precisam ser tomados. Mas vamos ver isto passo a passo, e também como corrigir algumas das limitações que irá com toda a certeza aparecer.
Observe este código 05, que pode ser visto 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 &arg) 17. { 18. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 19. } 20. //+----------------+ 21. stComplex operator+(const double arg) 22. { 23. return stComplex(m_r + arg, m_i); 24. } 25. //+----------------+ 26. stComplex operator+=(const double arg) 27. { 28. return stComplex(m_r += arg, m_i); 29. } 30. //+----------------+ 31. stComplex operator+=(const stComplex &arg) 32. { 33. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 34. } 35. //+----------------+ 36. bool operator<(const stComplex &arg) 37. { 38. return ((m_r < arg.m_r) ? true : (m_r > arg.m_r ? false : (m_i < arg.m_i))); 39. } 40. //+----------------+ 41. bool operator>(const stComplex &arg) 42. { 43. return ((m_r > arg.m_r) ? true : (m_r < arg.m_r ? false : (m_i > arg.m_i))); 44. } 45. //+----------------+ 46. stComplex Debug(uint arg) 47. { 48. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 49. return this; 50. } 51. //+----------------+ 52. }; 53. //+------------------------------------------------------------------+ 54. void OnStart(void) 55. { 56. stComplex a(1, 3); 57. int counter = 0; 58. 59. while (a < stComplex(5, 6)) 60. { 61. a.Debug(__LINE__); 62. a += stComplex(1, 3); 63. counter++; 64. if (counter > 50) break; 65. } 66. 67. a.Debug(__LINE__); 68. Print("The counter value is: ", counter); 69. } 70. //+------------------------------------------------------------------+
Código 05
Agora quero que você preste muita atenção, pois tudo que iremos fazer daqui para frente estará relacionado a este código 05. Você pode estar olhando e pensando: Cara, você é muito doido. Não estou conseguindo entender quase nada neste código 05. Mas isto que está sendo feito neste código 05, nada mais é do que uma outra forma de se escrever o código 04. Mas o que eu quero chamar a sua atenção é com relação as linhas 59 e 62.
Neste código 05, já temos meios para modificar a forma de trabalho da linha 62. E isto foi feito e mostrado nos artigos anteriores. Porém ainda não temos como fazer a mesma coisa com relação a linha 59. Ainda não. Porém, mesmo assim quero que você veja algumas coisas que podemos fazer aqui. Primeiro vamos executar este código, da forma como ele se apresenta logo acima. Com isto, o resultado é o que podemos visualizar na imagem logo abaixo.

Imagem 07
Certo. Agora vamos modificar a linha 59 para o que é visto logo abaixo.
while (stComplex(5, 6) > a)
E o resultado continuará sendo a imagem 07. Agora vamos modificar a linha 62, para o que é visto logo abaixo.
a += 1;Agora o resultado será o que podemos ver na imagem vista logo abaixo.

Imagem 08
Perceba que estamos podendo adicionar um valor inteiro, ou um valor do tipo stComplex, sem nenhum problema. E quanto ao teste para encerra o laço? Podemos fazer isto de qualquer maneira também? Bem a melhor forma de verificar isto, é na prática. Com isto, vamos agora modificar a linha 59 para o que é visto logo abaixo.
while (a < 5)
Pois bem, quando tentarmos compilar o código com esta mudança, veja o que irá ser reportado pelo compilador.

Imagem 09
O problema é que o compilador não sabe como comparar um tipo definido dentro da linguagem MQL5, com um tipo que nós mesmos estamos definindo. Precisamos ajudar o compilador a conseguir resolver este tipo de limitação. Para isto, vamos modificar o código 05, para o código visto logo abaixo, na íntegra.
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 double arg) 17. { 18. return stComplex(m_r + arg, m_i); 19. } 20. //+----------------+ 21. stComplex operator+=(const double arg) 22. { 23. return stComplex(m_r += arg, m_i); 24. } 25. //+----------------+ 26. bool operator<(const double arg) 27. { 28. return (m_r < arg); 29. } 30. //+----------------+ 31. bool operator>(const double arg) 32. { 33. return (m_r > arg); 34. } 35. //+----------------+ 36. stComplex operator+(const stComplex &arg) 37. { 38. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 39. } 40. //+----------------+ 41. stComplex operator+=(const stComplex &arg) 42. { 43. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 44. } 45. //+----------------+ 46. bool operator<(const stComplex &arg) 47. { 48. return ((m_r < arg.m_r) ? true : (m_r > arg.m_r ? false : (m_i < arg.m_i))); 49. } 50. //+----------------+ 51. bool operator>(const stComplex &arg) 52. { 53. return ((m_r > arg.m_r) ? true : (m_r < arg.m_r ? false : (m_i > arg.m_i))); 54. } 55. //+----------------+ 56. stComplex Debug(uint arg) 57. { 58. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 59. return this; 60. } 61. //+----------------+ 62. }; 63. //+------------------------------------------------------------------+ 64. void OnStart(void) 65. { 66. stComplex a(1, 3); 67. int counter = 0; 68. 69. while (a < 5) 70. { 71. a.Debug(__LINE__); 72. a += 1; 73. counter++; 74. if (counter > 50) break; 75. } 76. 77. a.Debug(__LINE__); 78. Print("The counter value is: ", counter); 79. } 80. //+------------------------------------------------------------------+
Código 06
Agora sim, podemos compilar este novo código. Com isto o resultado é o mostrado logo abaixo.

Imagem 10
Agora vem um tipo de limitação, na qual o MQL5 NÃO CONSEGUE RESOLVER. Na verdade, até onde sei, apenas a linguagem SMALLTALK é que consegue resolver este tipo de problema. Isto por conta de que tudo nela é um objeto. Literalmente ela é uma linguagem que levou a programação orientada em objeto ao seu extrema. Fora ela, desconheço qualquer outra que consiga. Se bem que existem tantas linguagens de programação que eu não conheça, que pode ser que alguma outra consiga. Apesar de eu ter estudado diversas linguagens ao longo dos anos. E sim meu caro leitor, consigo ler e entender códigos escritos em diversas linguagens.Mas apesar disto, não sou fluente na escrita em muitas delas, apenas em algumas poucas. Mas vamos voltar a nossa questão, assim o problema, é o que podemos visualizar no fragmento logo abaixo.
. . . 63. //+------------------------------------------------------------------+ 64. void OnStart(void) 65. { 66. stComplex a(1, 3); 67. int counter = 0; 68. 69. while (5 > a) 70. { 71. a.Debug(__LINE__); 72. a += 1; 73. counter++; 74. if (counter > 50) break; 75. } 76. 77. a.Debug(__LINE__); 78. Print("The counter value is: ", counter); 79. } 80. //+------------------------------------------------------------------+
Fragmento 04
Agora, quando você tentar compilar código 06, porém usando o que é visto neste fragmento 04, o compilador irá reportar este erro, visto logo abaixo.

Imagem 11
Por que este erro está acontecendo? Já que a princípio, temos o operador > sendo implementado para utilizar tipos inteiros, ou de ponto flutuante sem nenhum problema. E isto pode ser visto na linha 31 do código 06.
E a resposta para este erro é: Você talvez tenha se esquecido de como o compilador irá interpretar os operadores sobrecarregados. O problema aqui, não é se o operador está ou não sendo implementado. Mas sim, se ele pode ou não ser utilizado. Muitos podem estar pensando, mas é claro que não iria funcionar, o digito 5 não é uma variável. Assim o compilador não irá entender o que deve ser feito. Porém não é este o problema. Apesar da resposta está quase correta. Para demonstrar isto, você poderia utilizar o que é visto logo abaixo.
. . . 63. //+------------------------------------------------------------------+ 64. void OnStart(void) 65. { 66. stComplex a(1, 3); 67. int counter = 0; 68. 69. while (counter > a) 70. { 71. a.Debug(__LINE__); 72. a += 1; 73. counter++; 74. if (counter > 50) break; 75. } 76. 77. a.Debug(__LINE__); 78. Print("The counter value is: ", counter); 79. } 80. //+------------------------------------------------------------------+
Fragmento 05
Mesmo agora, teremos o mesmo tipo de erro sendo ainda reportado. Demonstrando assim, que o problema, não está em usar ou não uma variável. O problema está no tipo de informação que vem antes do operador sobrecarregado. Então para resolver este tipo de problema, precisamos fazer uma conversão de tipo. Isto a fim de transformar um tipo que está gerando o erro e um outro tipo. Desta maneira o compilador irá conseguir interpretar corretamente o código gerando assim o executável que poderemos utilizar.
De qualquer forma, a conversão deverá ser feita da mesma maneira, tanto do caso do fragmento 04, quanto no caso do fragmento 05. Mas tomando como exemplo o fragmento 04. O mesmo deveria ser modificado, para algo como o que é visto logo a seguir.
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): m_r(r), m_i(0) {} 15. //+----------------+ 16. stComplex(double r, double i): m_r(r), m_i(i) {} 17. //+----------------+ . . . 63. //+----------------+ 64. }; 65. //+------------------------------------------------------------------+ 66. void OnStart(void) 67. { 68. stComplex a(1, 3); 69. int counter = 0; 70. 71. while (stComplex(5) > a) 72. { 73. a.Debug(__LINE__); 74. a += 1; 75. counter++; 76. if (counter > 50) break; 77. } 78. 79. a.Debug(__LINE__); 80. Print("The counter value is: ", counter); 81. } 82. //+------------------------------------------------------------------+
Fragmento 06
Agora o compilador irá conseguir gerar o executável para nós. No entanto, perceba que para o compilador conseguir fazer isto, foi necessário adicionar um novo constructor na estrutura. Este é visto na linha 14. Sem este constructor sendo implementado, o compilador iria nos informar o seguinte erro mostrado logo abaixo.

Imagem 12
Note que junto do erro que está sendo informado, o compilador, nos informa de maneira bastante adequada, quais são os constructors presentes na estrutura. Nos permitindo tomar as devidas providências, ou ajustes no código que estamos tentando gerar. E no final o resultado é o que podemos ver logo a seguir.

Imagem 13
Ok, acho que entendi como corrigir este tipo de problema. Mas e quanto aos demais operadores lógicos? Como seria a forma de se trabalhar com eles? Será que você poderia me mostrar um exemplo, apenas para que eu venha a ter um ponto de partida, caso precise um dia implementar algo relacionado a eles? Mas é claro que posso lhe mostrar algum exemplo. Na verdade, vou passar por quase todos os operadores. Se bem que não irei demonstrar um por um, já que ao meu entender isto seria desnecessário. Mas ainda assim quero mostrar como podemos usar a sobrecarga de modo que você tenha um ponto de partida, e de estudo.
Assim sendo, vamos ver outros dois operadores, logo de cara. Estes estão sendo implementados no código logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. struct stComplex 005. { 006. //+----------------+ 007. private : 008. double m_r, m_i; 009. //+----------------+ 010. public : 011. //+----------------+ 012. stComplex(): m_r(0), m_i(0) {} 013. //+----------------+ 014. stComplex(double r): m_r(r), m_i(0) {} 015. //+----------------+ 016. stComplex(double r, double i): m_r(r), m_i(i) {} 017. //+----------------+ 018. stComplex operator+(const double arg) 019. { 020. return stComplex(m_r + arg, m_i); 021. } 022. //+----------------+ 023. stComplex operator+=(const double arg) 024. { 025. return stComplex(m_r += arg, m_i); 026. } 027. //+----------------+ 028. bool operator<(const double arg) 029. { 030. return (m_r < arg); 031. } 032. //+----------------+ 033. bool operator>(const double arg) 034. { 035. return (m_r > arg); 036. } 037. //+----------------+ 038. stComplex operator+(const stComplex &arg) 039. { 040. return stComplex(m_r + arg.m_r, m_i + arg.m_i); 041. } 042. //+----------------+ 043. stComplex operator+=(const stComplex &arg) 044. { 045. return stComplex(m_r += arg.m_r, m_i += arg.m_i); 046. } 047. //+----------------+ 048. bool operator<(const stComplex &arg) 049. { 050. return ((m_r < arg.m_r) ? true : (m_r > arg.m_r ? false : (m_i < arg.m_i))); 051. } 052. //+----------------+ 053. bool operator>(const stComplex &arg) 054. { 055. return ((m_r > arg.m_r) ? true : (m_r < arg.m_r ? false : (m_i > arg.m_i))); 056. } 057. //+----------------+ 058. bool operator&&(const stComplex &arg) 059. { 060. return m_r && arg.m_r && m_i && arg.m_i; 061. } 062. //+----------------+ 063. stComplex operator&(const stComplex &arg) 064. { 065. union u1 066. { 067. double p64; 068. ulong u64; 069. }v_r0, v_i0, v_r1, v_i1; 070. 071. v_r0.p64 = m_r; 072. v_i0.p64 = m_i; 073. v_r1.p64 = arg.m_r; 074. v_i1.p64 = arg.m_i; 075. 076. v_r0.u64 &= v_r1.u64; 077. v_i0.u64 &= v_i1.u64; 078. 079. return stComplex(v_r0.p64, v_i0.p64); 080. } 081. //+----------------+ 082. stComplex Debug(uint arg) 083. { 084. PrintFormat("Debugging the line %d = %.02f %c %.02fi", arg, m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 085. return this; 086. } 087. //+----------------+ 088. }; 089. //+------------------------------------------------------------------+ 090. void OnStart(void) 091. { 092. stComplex a(15, 49), 093. b(7, 25); 094. 095. a.Debug(__LINE__); 096. b.Debug(__LINE__); 097. 098. Print("The result of A & B is:"); 099. (a & b).Debug(__LINE__); 100. 101. Print("The result of A && B is: ", (a && b ? "TRUE" : "FALSE")); 102. } 103. //+------------------------------------------------------------------+
Código 07
Antes de explicar este código 07, que tão ver o resultado da execução do mesmo? Pois bem, isto pode ser visto na imagem logo abaixo.

Imagem 14
Este tipo de resultado pode parecer um pouco estranho, caso você não entenda como algumas outras coisas funcionam. Como tudo que está sendo feito aqui, neste código 07, já foi demonstrado e explicado em artigos anteriores, dentro desta mesma sequência de artigos. Não irei repetir o que já foi dito antes. Na dúvida procure ler os artigos anteriores para maiores detalhes. Porém, existe uma questão da qual não abordei em nenhum dos artigos anteriores. E esta questão permite justificar o resultado que está sendo mostrado nesta imagem 14.
No entanto, existe um pequeno detalhe, que para ser melhor entendido, será necessário que você tenha de fato compreendido o conteúdo de um outro artigo. Do qual muitos talvez não deram o devido valor, ao conteúdo que está sendo explicado ali. O artigo em questão é Do básico ao intermediário: Ponto Flutuante. Isto por que, para entender o resultado impresso pela linha 99 do código 07, é necessário entender justamente aquele conteúdo, onde explico como interpretar um valor em ponto flutuante. E isto usando os próprios bits presentes e que formam um valor em ponto flutuante.
Como eu venho frisando meu caro leitor, nenhum artigo nesta sequência é inútil ou tem pouco valor. Cada um foi pensado para ser postado em um determinado momento. De modo que as coisas vão ficando cada vez mais complicadas de maneira gradativa. Porém, para em vem estudando os artigos, isto não será percebido. Já que os assuntos acabam se ligando uns aos outros. Portanto, procure estudar cada coisa que está sendo explicada, no seu devido tempo. Não precisa ter presa. Apenas procure compreender cada conceito a fim de entender futuras publicações que serão feitas.
Dado este recado podemos continuar. Sendo que existem algo que podemos fazer a fim de melhorar um pouco a apresentação das coisas. Isto por que, olhando a imagem 14, fica um tanto quanto estranho entender o que está acontecendo. Assim, vamos promover a mudança mostrada no fragmento logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. struct stComplex 005. { . . . 081. //+----------------+ 082. string ToString(void) 083. { 084. return StringFormat("%.02f %c %.02fi", m_r, (m_i < 0 ? '-' : '+'), MathAbs(m_i)); 085. } 086. //+----------------+ 087. stComplex Debug(uint arg) 088. { 089. Print("Debugging the line ", arg, " = ", this.ToString()); 090. return this; 091. } 092. //+----------------+ 093. }; 094. //+------------------------------------------------------------------+ 095. void OnStart(void) 096. { 097. stComplex a(15, 49), 098. b(7, 25); 099. Print("Variable A: ", a.ToString()); 100. Print("Variable B: ", b.ToString()); 101. Print("The result of A & B is: ", (a & b).ToString()); 102. Print("The result of A && B is: ", (a && b ? "TRUE" : "FALSE")); 103. } 104. //+------------------------------------------------------------------+
Fragmento 07
Agora quando executado, o resultado será este, visto na imagem logo abaixo.

Imagem 15
Agora sim, ficou bem mais simples entender as coisas. Mas vamos voltar a nossa questão. Por que A & B está dando este resultado? Isto não faz o menor sentido. Bem, talvez não faça para você, meu caro amigo leitor, que caiu de paraquedas neste artigo. Mas para quem vem estudando os artigos desta sequência, este resultado faz todo sentido. Visto que ali não estamos trabalhando ou fatorando um valor do tipo ponto flutuante. Mas sim um valor do tipo inteiro. Hã? Espere um pouco. Agora eu que venho acompanhando os artigos é que fiquei confuso. Como assim estamos fatorando um valor do tipo inteiro? A estrutura não está utilizando valores do tipo double? Como podemos ver sendo declarado na linha oito do código 07?
Sim, meu caro leitor, a estrutura trabalha com valores do tipo double, que são ponto flutuante de precisão dupla. Porém, toda via e, entretanto, o compilador NÃO sabe como fazer uma operação bit a bit com este tipo de valor. Precisamos converter este tipo em um tipo que o compilador saiba como trabalhar. No caso o tipo mais adequado é um inteiro de 64 bits. Por isto o resultado está sendo este que você está vendo nas imagens 14 e 15.
Ainda não consegui entender. Poderia explicar isto melhor? Sem problemas meu caro leitor. Então para entender isto de uma maneira definitiva. Vamos criar um pequeno código, que irá demonstrar o que está acontecendo. O código inicial é mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. double v1 = 15, 07. v2 = 7, 08. v3; 09. 10. v3 = v1 & v2; 11. 12. Print(v3); 13. } 14. //+------------------------------------------------------------------+
Código 08
Quando você tentar compilar este código, irá ver o compilador reportar o seguinte erro, mostrado logo abaixo.

Imagem 16
Este erro se deve ao fato de que, o compilador NÃO SABE como fazer uma operação AND BIT A BIT. Isto quando os valores estão no formato ponto flutuante. Para resolver isto, precisamos utilizar um mecanismo que nos permita converter valores de ponto flutuante para valores inteiros. Pois o compilador sabe como fazer um AND BIT A BIT com inteiros. Pois bem, você poderia pensar em fazer o que é visto no código logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. double v1 = 15, 07. v2 = 7, 08. v3; 09. ulong u1; 10. 11. u1 = ulong(v1) & ulong(v2); 12. v3 = double(u1); 13. 14. Print(v3); 15. } 16. //+------------------------------------------------------------------+
Código 09
De fato, aqui nas linhas onze e doze estamos fazendo uma conversão de tipo. O que permite o compilador criar o executável. Porém quero que você observe uma coisa neste código que estamos criando. Os valores de v1 e v2 são exatamente a parte real que foi utilizada nos códigos anteriores. Sendo assim deveríamos obter um resultado igual ao que foi gerado na sobrecarga do operador AND bit a bit. Bem, isto é o que presumimos que deveria ocorrer. No entanto, quando você executar este código 09, o resultado é o que pode ser visto logo abaixo.

Imagem 17
Mas o que aconteceu aqui? Por que os resultados foram diferentes? O motivo para isto foi explicado em outros artigos, meu caro leitor. Onde expliquei como podemos manipular a memória a nosso favor. Um dos artigos em questão pode ser visualizado em Do básico ao intermediário: União (I). Nestes artigos sobre uniões, foi explicado e demonstrado, como podemos criar e manipular a memória sem que tenhamos perda de informação. No caso de transformarmos um valor do tipo ponto flutuante em um valor do tipo inteiro. Temos uma perda de informação. Isto devido ao fato de que a parte fracionaria do valor em ponto flutuante será descartada quando a conversão for executada. É justamente este detalhe, que não foi explicado na época quando falei de uniões.
Certo, agora estou começando a entender por que daquele resultado apresentado. Mas será que existe alguma forma de trabalhar com valores em ponto flutuante mais facilmente? Pois ficar fazendo aquelas divisões e multiplicações, que forma mostradas quando você explicou como converter um valor decimal em ponto flutuante é muito chato e cansativo. Deve existir uma maneira mais simples de se fazer as coisas. E sim meu caro leitor, existe uma maneira mais simples de se observar o conteúdo interno da memória de um valor em ponto flutuante. O único detalhe é que você precisa saber entender este valor que será mostrado. E para isto aquele artigo, onde explico o modelo de ponto flutuante, é algo extremamente necessário. Mas supondo que você de fato tenha compreendido o que foi mostrado ali, podemos fazer isto agora. Então modificando novamente o código 09, podemos criar algo parecido com o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union _u 07. { 08. ulong u64; 09. double pf64; 10. }v1, v2, v3; 11. 12. v1.pf64 = 15; 13. v2.pf64 = 7; 14. v3.u64 = v1.u64 & v2.u64; 15. 16. Print(v3.pf64); 17. } 18. //+------------------------------------------------------------------+
Código 10
Ok, agora quando este código for executado, você irá ver o que é mostrado logo abaixo.

Imagem 18
Legal, agora sim o resultado está batendo com o que foi visto anteriormente. No entanto, ainda estou querendo saber se podemos ver o conteúdo da memória. Me esqueci deste detalhe. Me perdoe meu caro leitor. Então o código para você poder observar cada mínimo detalhe é mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union _u 07. { 08. ulong u64; 09. double pf64; 10. }v1, v2, v3; 11. 12. #define PrintContent(x) PrintFormat("Memory content in variable %s is: %I64X", #x, x.u64) 13. 14. v1.pf64 = 15; 15. v2.pf64 = 7; 16. v3.u64 = v1.u64 & v2.u64; 17. 18. PrintContent(v1); 19. PrintContent(v2); 20. PrintContent(v3); 21. Print("The result of v1 & v2 is: ", v3.pf64); 22. } 23. //+------------------------------------------------------------------+
Código 11
Este código 11, quando executado irá produzir o que é mostrado na imagem a seguir.

Imagem 19
E então, agora conseguiu entender o que está acontecendo, meu caro leitor? Note que se o código não fosse implementado da maneira adequada, acabaríamos ficando com um resultado completamente equivocado. O que mais hora ou menos hora, acabaria por gerar uma quantidade enorme de problemas. Dos quais poderia vir a comprometer todo o código que viesse a ser implementado daquele ponto em diante.
Sei que para muitos, o que foi visto aqui pode parecer um tanto quanto estranho. Mas como o objetivo aqui é ser didático, e não o de produzir nenhum tipo de aplicação. O que foi visto aqui, deve ser utilizado apenas para tais fins. Por conta disto, que estou sempre começando os artigos, sempre com a mesma frase. Mostrando que qualquer coisa criada aqui, visa somente a didática.
Isto por que, dependendo do tipo de trabalho que você vier a precisar fazer, terá que adaptar este conteúdo a suas necessidades. Então não procure decorar código. Procure entender como ele funciona e que tipo de conceito foi utilizado ali. Pois isto sim irá lhe server em toda e qualquer tipo de situação.
Considerações finais
Neste artigo foi demonstrado como poderíamos implementar a sobrecarga tanto de operadores lógicos como também de operadores relacionais. Fazer isto demanda um certo cuidado e uma boa dose de atenção. Já que um simples deslize durante a implementação do que será a sobrecarga de tais operadores, pode vir a pôr todo um código em condição de ser totalmente jogado no lixo. Já que se a sobrecarga vier a ter problemas. Toda uma base de dados criada em cima dos resultados gerados pelo seu código deverá ser completamente descartada, ou no mínimo totalmente revisada. Além é claro, toda uma série de relações que foram geradas.
Por isto, o conteúdo visto neste artigo precisa ser muito bem estudado e assimilado. Visto que aqui criamos uma vasta relação entre diversos conceitos e conhecimento. Que muitos consideram ser desnecessário. No entanto, os mesmos se provaram ser de grande utilidade para que os resultados, durante os testes, pudessem realmente ser atestado como corretos. Espero que você tenha gostado do conteúdo visto aqui. E no próximo artigo iremos tratar da sobrecarga de outros operadores. Então até breve, e nos vemos lá.
| Arquivo MQ5 | Descriçã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 |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Simulação de mercado: A união faz a força (II)
Previsão de barras Renko com a ajuda de IA CatBoost
Desenvolvendo um EA multimoeda (Parte 26): Informador para instrumentos de negociação
Análise espectral singular unidimensional
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso