Do básico ao intermediário: Sobrecarga de operadores (IV)
Introdução
No artigo anterior Do básico ao intermediário: Sobrecarga de operadores (III), foi explicado e demonstrado como deveríamos proceder quando vier a ser necessário implementar a sobrecarga de operadores lógicos e relacionais. Apesar de toda a simplicidade que estou tentando colocar nos artigos, a fim de que todos consigam entender como criar suas próprias soluções. Este tema relacionado a sobrecarga de operadores é um dos que mais gera confusão. Isto quando está diretamente relacionado a programadores iniciantes. Então meu caro leitor, não fique achando que já sabe como fazer as coisas, somente por conta de ter lido aquele artigo anterior. É muito importante que você procure praticar e experimentar diversos tipos de situações a fim de realmente conseguir assimilar adequadamente aquele tema.
Pois bem, se a questão dos operadores lógicos e relacionais, já causa uma boa margem para confusão e problemas. O que será visto aqui, acaba tornando as coisas ainda bem mais confusas. Isto por que, aqui iremos tratar de um outro tipo de operador a ser sobrecarregado. E estou falando do operador subscrito, e como ele dificilmente aparece de forma isolada, iremos também tratar do operador de atribuição. Então chegou a hora de dar um tempo em todas as coisas que possam vir a tirar a sua atenção. E procurar focar no que será visto neste artigo. Pois como alguns costumam dizer: Agora o bicho vai pegar.
Sobrecarga de operadores (IV)
Dentro daquilo que o MQL5, nos permite fazer, o que será visto aqui irá fechar o tema sobrecarga de operadores. Pelo menos no que diz respeito a parte fácil referente ao tema. Já que existem algumas outras coisas que podemos fazer, que faz com que este assunto relacionado a sobrecarga de operadores, venha a ser tornar algo bem mais avançado e que pode confundir muito iniciante. Mas isto iremos ver conforme novos artigos irão sendo escritos e postados. Apesar de tudo, no meu outro perfil de artigos, iremos tratar de uma forma bem legal de utilizar está sobrecarga. Mas isto é assunto para outro momento.
Ok, o grande detalhe do operador [], que também é conhecido como operador subscrito, é que ele normalmente não será implementado de maneira isolada. Normalmente quando sobrecarregamos este operador, também precisamos sobrecarregar o operador de atribuição. Isto por que, ou estaremos atribuindo um valor a algum tipo de index, ou estaremos lendo um valor de algum tipo de index. Que qualquer forma não devemos descartar a possibilidade e possível necessidade de implementar dois operadores de uma só fez.
Como este operador subscrito, pode vir a ser bastante confuso. Vou tentar mostrar como se faz a sobrecarga do mesmo, de maneira que todos consigam entender o que está sendo feito. Então vamos começar com algo muito simples mesmo. E isto pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stList 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. int value; 10. //+----------------+ 11. public : 12. //+----------------+ 13. stList *operator[](const uchar arg) 14. { 15. Print("Value ", arg, " was passed as being an index to the operator []"); 16. 17. return GetPointer(this); 18. } 19. //+----------------+ 20. }; 21. //+------------------------------------------------------------------+ 22. void OnStart(void) 23. { 24. stList demo; 25. 26. demo[4] = 10; 27. } 28. //+------------------------------------------------------------------+
Código 01
Se você tentar compilar este código 01, que não faz absolutamente nada, irá ver que o compilar irá se queixar de diversas coisas. Como pode ser observado na imagem logo abaixo.

Imagem 01
Nesta imagem 01, temos alguns problemas que é muito importante que você os entenda muito bem, meu caro leitor. O primeiro problema está relacionado ao erro mostrado na primeira linha da imagem 01. Este erro é decorrente do fato de que na linha 17, do código 01, estamos retornando uma referência à classe, declarada na linha quatro. No entanto, esta referência, irá estar ligada diretamente a variável declarada na linha nove. Porém, toda via e, entretanto, não é prudente retornarmos uma referência a alguma variável privativa da classe. Fazer isto quebra o princípio do encapsulamento, tornando o código extremamente perigoso e difícil de ser corrigido em caso de falhas. O que temos de fazer é de fato retornar algum tipo de ponteiro que poderemos utilizar depois.
Pois bem, nos artigos anteriores vimos como poderíamos resolver este tipo de erro mostrado na imagem 01. No entanto, não iremos fazer isto aqui. Iremos sim seguir por um outro caminho que é um pouco mais complicado neste primeiro momento. Mas que conforme o tempo for passando e você praticando o que será visto aqui, se tornará cada vez mais simples de ser seguido.
Muito bem, os dois últimos erros nesta imagem 01, estão relacionados diretamente ao operador de atribuição. Isto por que, o compilador não sabe exatamente como agir neste tipo de situação. Lembrando que aqui estaremos seguindo um caminho diferente. E sendo assim, as coisas podem não fazer muito sentido neste primeiro momento. Mas então, vamos mudar o código 01, para o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stList 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. int value; 10. //+----------------+ 11. public : 12. //+----------------+ 13. stList *operator[](const uchar arg) 14. { 15. Print("Value ", arg, " was passed as being an index to the operator []"); 16. 17. return GetPointer(this); 18. } 19. //+----------------+ 20. void operator=(const int arg) 21. { 22. Print("Value ", arg," is being assigned to the internal variable of the class."); 23. } 24. //+----------------+ 25. }; 26. //+------------------------------------------------------------------+ 27. void OnStart(void) 28. { 29. stList demo; 30. 31. demo[4] = 10; 32. } 33. //+------------------------------------------------------------------+
Código 02
Agora sim, o compilador irá conseguir gerar um executável. Então executando ele no terminal do MetaTrader 5, iremos obter o que é visto logo abaixo, como resposta.

Imagem 02
Ok, isto não parece ser lá grandes coisas. Mas é. E vou explicar o motivo, para você tentar entender o que aconteceu aqui. Ao olhar a linha 31, você se depara com algo muito comum, para quem já sabe como trabalhar com arrays. Porém, apesar desta aparente semelhança, aqui estamos fazendo algo muito diferente do que seria trabalhar com simples arrays.
Por conta disto é importante entender o que está se passando aqui no código 02, antes de darmos o que será o próximo passo. Note que quando o código 02 é executado, primeiro é executado o código da linha 13. Para somente depois ser executado o código da linha 20. Entender isto é importante, para conseguir entender a própria sobrecarga que estará sendo feita. Pois dependendo do que você esteja tentando fazer, esta sequência pode vir a ser diferente. Então atenção a estes pequenos detalhes.
Muito bem, então vamos ao próximo detalhe. Iremos fazer isto bem devagar neste início, para que você consiga entender cada pequeno detalhe que estará sendo feito aqui. O próximo passo é entender o que esta linha 31 no código 02 está de fato fazendo. Isto pode ser observado vendo o fragmento logo abaixo.
. . . 26. //+------------------------------------------------------------------+ 27. void OnStart(void) 28. { 29. stList demo; 30. 31. demo.operator[](4).operator=(10); 32. } 33. //+------------------------------------------------------------------+
Fragmento 01
Neste fragmento temos a forma como compilador irá enxergar o código visto dentro do procedimento OnStart. Isto olhando para o código 02. Entender e ter esta noção é muito importante para entender por que o código que será visto mais à frente de fato funciona.
Legal, então o próximo passo é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stList 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. int value; 10. //+----------------+ 11. public : 12. //+----------------+ 13. stList *operator[](const uchar arg) 14. { 15. Print("Value ", arg, " was passed as being an index to the operator []"); 16. 17. return GetPointer(this); 18. } 19. //+----------------+ 20. stList *operator=(const int arg) 21. { 22. value = arg; 23. 24. return GetPointer(this); 25. } 26. //+----------------+ 27. void Debug(const int arg) 28. { 29. Print("Debugging line ", arg); 30. Print(value); 31. } 32. //+----------------+ 33. }; 34. //+------------------------------------------------------------------+ 35. void OnStart(void) 36. { 37. stList demo; 38. 39. (demo[4] = 10).Debug(__LINE__); 40. } 41. //+------------------------------------------------------------------+
Código 03
E o resultado da execução é o que podemos ver na imagem logo abaixo.

Imagem 03
Perfeito, temos um código que está funcionando dentro daquilo que podemos fazer de maneira simples. Porém antes de avançarmos para algo mais elaborado, quero explicar alguns detalhes que talvez venha a fazer a diferença para você, no futuro, meu caro leitor. Um destes detalhes, tem a ver justamente com o operador de atribuição. Porém para que você possa realmente entender o que desejo explicar, precisamos fazer com que o operador subscrito venha a ter algum tipo de funcionalidade prática. Mesmo que ela seja bem simples. Por conta disto, vamos modificar o código de forma que ele seja de alguma forma utilizável. Assim, surge o código visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. union un_0 10. { 11. ulong u64; 12. uchar u8[8]; 13. }m_info; 14. //+----------------+ 15. public : 16. //+----------------+ 17. void operator=(const ulong arg) 18. { 19. m_info.u64 = arg; 20. } 21. //+----------------+ 22. void Debug(const int arg) 23. { 24. PrintFormat("Info is 0x%I64X", m_info.u64); 25. } 26. //+----------------+ 27. }; 28. //+------------------------------------------------------------------+ 29. void OnStart(void) 30. { 31. stDemo demo; 32. 33. demo = 0x1020304050607080; 34. 35. demo.Debug(__LINE__); 36. } 37. //+------------------------------------------------------------------+
Código 04
Quando executado este código 04, irá gerar como resultado o que é visto logo abaixo.

Imagem 04
Note que neste caso, do código 04, o a sobrecarga do operador de atribuição foi feita de tal forma, que apenas e somente uma única atribuição poderá ser feita. Assim como também não poderemos chamar o procedimento de depuração diretamente na linha que estamos efetuando a atribuição. Como você pode notar, podemos fazer as coisas assim. Contudo, normalmente não é bem isto que fazemos na prática, meu caro leitor. Na prática, normalmente retornando algum tipo de referência para podermos efetuar atribuições em cascata. Iremos chegar a este ponto em breve.
Porém, quero que você veja uma outra coisa antes. Observe que na linha nove estamos definindo uma união. E dentro desta união, temos um array do tipo estático. Agora preste atenção ao que iremos fazer. Pois isto irá server de apoio para podermos explicar alguns detalhes interessantes sobre o uso do operador subscrito junto com o operador de atribuição. Então modificando o código mais uma vez, temos o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. union un_0 10. { 11. ulong u64; 12. uchar u8[8]; 13. }m_info; 14. uchar m_index; 15. //+----------------+ 16. public : 17. //+----------------+ 18. stDemo *operator[](const uchar arg) 19. { 20. m_index = arg; 21. return GetPointer(this); 22. } 23. //+----------------+ 24. void operator=(const ulong arg) 25. { 26. m_info.u64 = arg; 27. } 28. //+----------------+ 29. void Debug(const int arg) 30. { 31. PrintFormat("Info is 0x%I64X", m_info.u64); 32. } 33. //+----------------+ 34. }; 35. //+------------------------------------------------------------------+ 36. void OnStart(void) 37. { 38. stDemo demo; 39. 40. demo = 0x1020304050607080; 41. demo.Debug(__LINE__); 42. demo[1] = 0xFA; 43. demo.Debug(__LINE__); 44. } 45. //+------------------------------------------------------------------+
Código 05
Ao executar este código 05, o resultado é o que vemos logo a seguir.

Imagem 05
Obviamente não é isto que que estamos vendo na imagem 05 é que queríamos produzir como resultado. Mas então, por que este foi o resultado produzido? O motivo é justamente o fato de que não estamos sobrecarregando a sobrecarga do operador de atribuição. Mas como assim? Isto pareceu ser um tanto quanto confuso para mim. Na verdade isto é bem mais simples do que possivelmente esteja imaginando meu caro leitor. Entenda o seguinte: Quando o compilador tentar utilizar o operador de atribuição ele irá estará vendo apenas o que estamos definindo na linha 24. Porém se o compilador faz isto, ele estará forçando as coisas a um modo de uso como é visto na linha 40. E não como desejamos fazer na linha 42. Para mudar isto, precisamos fazer com que o código fique como mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. union un_0 10. { 11. ulong u64; 12. uchar u8[8]; 13. }m_info; 14. uchar m_index; 15. //+----------------+ 16. public : 17. //+----------------+ 18. stDemo *operator[](const uchar arg) 19. { 20. m_index = arg; 21. return GetPointer(this); 22. } 23. //+----------------+ 24. void operator=(const uchar arg) 25. { 26. m_info.u8[m_index] = arg; 27. } 28. //+----------------+ 29. void operator=(const ulong arg) 30. { 31. m_info.u64 = arg; 32. } 33. //+----------------+ 34. void Debug(const int arg) 35. { 36. PrintFormat("Info is 0x%I64X", m_info.u64); 37. } 38. //+----------------+ 39. }; 40. //+------------------------------------------------------------------+ 41. void OnStart(void) 42. { 43. stDemo demo; 44. 45. demo = 0x1020304050607080; 46. demo.Debug(__LINE__); 47. demo[1] = 0xFA; 48. demo.Debug(__LINE__); 49. } 50. //+------------------------------------------------------------------+
Código 06
Bem veja agora que temos uma sobrecarga da própria sobrecarga do operador sendo criada.
Para mais detalhe de como uma sobrecarga funciona e como ela pode ser implementada, veja o artigo Do básico ao intermediário: Sobrecarga, nele foi explicado com detalhes todos os princípios e conceitos básicos envolvidos na implementação do que seria uma sobrecarga. Ok, aparentemente o código parece estar dentro dos conforme, podendo assim, a princípio gerar o resultado que desejamos. Porém, toda via e, entretanto, quando você tentar compilar este código 06 irá receber o seguinte tipo de mensagem do compilador.

Imagem 06
Mas que coisa mais doida é esta? Não entendi por que o compilador estaria reportando este tipo de erro. Já que aparentemente o código está perfeitamente implementado. De fato, meu caro leitor, o código, aos nossos olhos está corretamente implementado. Porém o compilador não o está entendendo. E o motivo está na própria imagem 06. Veja que o compilador, não sabe exatamente qual função ou procedimento chamar para que a linha 47 seja devidamente compilada. Isto por conta da própria sobrecarga da sobrecarga do operador de atribuição.
Sei que a princípio este tipo de coisa é um tanto quanto estranho. Isto devido justamente ao que foi explicado no artigo onde abordamos a sobrecarga de funções e procedimentos. No entanto, corrigir esta falha vista na imagem 06, é algo tão simples como andar para frente. Tudo que precisamos fazer é dizer ao compilador qual das sobrecargas deverá ser utilizada. Para fazer isto, mudamos o código na linha 47, como mostrado no fragmento logo abaixo.
. . . 40. //+------------------------------------------------------------------+ 41. void OnStart(void) 42. { 43. stDemo demo; 44. 45. demo = 0x1020304050607080; 46. demo.Debug(__LINE__); 47. demo[1] = (uchar) 0xFA; 48. demo.Debug(__LINE__); 49. } 50. //+------------------------------------------------------------------+
Fragmento 02
Agora sim, o compilador irá conseguir criar o executável. Assim ao executarmos este executável, iremos ter como resposta o que é visto na imagem logo abaixo.

Imagem 07
Legal, agora temos um ponto de partida para entender diversas outras coisas, relacionadas a estes dois operadores. Mas talvez você esteja pensando que o índice está errado. Se você chegou a esta conclusão, tudo que precisa ser feito, é mudar o código como mostrado no fragmento logo abaixo.
. . . 23. //+----------------+ 24. void operator=(const uchar arg) 25. { 26. m_info.u8[sizeof(m_info) - m_index - 1] = arg; 27. } 28. //+----------------+ . . .
Fragmento 03
Agora quando o código voltar a ser executado, o resultado será o que podemos ver logo abaixo.

Imagem 08
Muito legal, não é mesmo meu caro leitor? Porém, isto que vimos aqui é apenas a parte básica e sem graça do uso da sobrecarga de tais operadores. Então que tal vermos algo um pouco mais divertido? Onde poderemos ir um pouco mais além?
Pois bem, no artigo Do básico ao intermediário: Filas, Listas e Árvores (IV), foi demonstrado como poderíamos implementar uma lista duplamente encadeada. Naquele código temos a possibilidade de fazer algo que naquele momento não seria possível. Isto por que, ainda não tínhamos os mecanismos que foram vistos aqui neste artigo. E o mecanismo no qual estou me referindo é justamente este da sobrecarga dos operadores de subscrito e de atribuição. Porém existe um pequeno detalhe que nos força a fazer algo um tanto quanto diferente. Mas vamos com calma para que as coisas venham a fazer sentido e não fiquem mais complicadas do que o necessário.
Sendo assim, vamos pegar um dos códigos vistos no artigo acima mencionado. Isto para que possamos entender como usar a sobrecarga naquele tipo de situação. O código a ser utilizado é mostrado logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. template <typename T> class stList 005. { 006. private: 007. //+----------------+ 008. T info; 009. stList <T> *prev, 010. *start; 011. uint counter; 012. //+----------------+ 013. public: 014. //+----------------+ 015. stList(void) 016. :prev(NULL), 017. start(NULL), 018. counter(0) 019. {} 020. //+----------------+ 021. void Store(T arg, const uint index = 0xFFFFFFFF) 022. { 023. stList <T> *loc, 024. *ptr1 = start, 025. *ptr2 = NULL; 026. 027. for (uint c = 0; (ptr1 != NULL) && (c < index); ptr2 = ptr1, ptr1 = (*ptr1).start, c++); 028. 029. loc = new stList <T>; 030. (*loc).info = arg; 031. (*loc).start = (ptr2 != NULL ? (*ptr2).start : ptr1); 032. (*loc).prev = (ptr1 != NULL ? (*ptr1).prev : ptr2); 033. if (ptr2 != NULL) (*ptr2).start = loc; else start = loc; 034. if (ptr1 != NULL) (*ptr1).prev = loc; else prev = loc; 035. 036. counter++; 037. } 038. //+----------------+ 039. bool Restore(T &arg, const uint index = 0xFFFFFFFF) 040. { 041. if ((prev == NULL) || (start == NULL)) 042. return false; 043. 044. stList <T> *loc = (index < counter ? start : prev), 045. *ptr = NULL; 046. 047. for (uint c = 0; (loc != NULL) && (c < index) && (index < counter); ptr = loc, loc = (*loc).start, c++); 048. if (loc == NULL) return false; 049. 050. if (index == 0) 051. { 052. start = (*loc).start; 053. if (start != NULL) (*start).prev = NULL; 054. } else if (index >= (counter - 1)) 055. { 056. prev = (*loc).prev; 057. if (prev != NULL) (*prev).start = NULL; 058. } 059. else 060. { 061. (*ptr).start = (*loc).start; 062. (*loc).start.prev = ptr; 063. } 064. arg = (*loc).info; 065. delete loc; 066. counter--; 067. 068. return true; 069. } 070. //+----------------+ 071. bool Exclude(const uint index) 072. { 073. T tmp; 074. 075. return Restore(tmp, index); 076. } 077. //+----------------+ 078. void Debug(void) 079. { 080. Print("===== DEBUG ====="); 081. for (stList <T> *loc = start; loc != NULL; loc = (*loc).start) 082. PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).start, loc, (*loc).prev, (*loc).info); 083. Print("================="); 084. } 085. //+----------------+ 086. }; 087. //+------------------------------------------------------------------+ 088. void OnStart(void) 089. { 090. stList <char> list; 091. 092. list.Store(10); 093. list.Store(84); 094. list.Store(-6); 095. list.Store(47, 0); 096. 097. list.Debug(); 098. 099. list.Exclude(3); 100. list.Store(35, 2); 101. 102. list.Debug(); 103. 104. for (char info; list.Restore(info, 0);) 105. Print(info); 106. }; 107. //+------------------------------------------------------------------+
Código 07
Ao executarmos este código 07, teremos como resposta o que é visto na imagem logo abaixo.

Imagem 09
Agora preste atenção meu caro leitor, pois este ponto é muito importante. Este código 07 cria uma lista, conforme foi visto no artigo mencionado acima. No entanto, usar a sobrecarga de operadores aqui de maneira direta, tem consequências. Isto se você não vir a modificar de maneira adequada a forma como a lista encadeada estará sendo implementada. Isto talvez possa parecer ser algo um tanto quanto contraditório. Já que o código 07 consegue trabalhar de maneira adequada. Porém, da forma como ele se encontra, se você tentar implantar a sobrecarga de operadores de qualquer maneira, acabará tendo um grande problema nas mãos. Ou no mínimo algo que não venha realmente a ser muito útil na prática.
Implementar todas as mudanças necessárias, exige que seja explicado um outro tipo de detalhe que do qual ainda não falamos. No entanto, e contudo, podemos fazer uma implementação mais básica da sobrecarga vista neste artigo, a fim de tornar este código 07, pelo menos um pouco mais fácil de ser entendido a qualquer momento. Já que a princípio as mudanças que precisaríamos implementar, tornaria as linhas 95 e 100 um pouco mais fáceis de serem compreendidas. Porém isto tem um custo.
Portanto o resultado que iremos ver, pelo menos neste início será um pouco diferente do que o mostrado na imagem 09. Mas conforme formos avançando, e for explicado um outro detalhe, que também podemos utilizar aqui. O resultado final será exatamente o que podemos ver na imagem 09. Mas o código presente no procedimento OnStart, estará completamente diferente deste que você pode observar ao olhar o código 07. Além é caro grande parte do código presente na classe stList. Isto por conta o uso mais massivo da sobrecarga de operadores.
Sendo assim, vamos começar a efetuar as mudanças. Sendo a primeira das mudanças a serem feitas, será a remoção da função Exclude, presente na linha 71 deste código 07. Mas por que remover esta função, neste momento? O motivo é que esta remoção será apenas temporária. E por mais estranho que possa parecer. Esta função Exclude, dificulta o processo de acesso e indicação do índice dentro do operador de subscrito. Tornando os resultados bastante mais complicados de serem compreendido. No entanto, depois iremos retornar com esta função Exclude. Mas com um formato de uso, um pouco diferente.
Ok. Então o novo código pode ser visto logo abaixo. Sendo este um código de transição, entre o código 07 e o código que iremos de fato obter mais para frente.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. template <typename T> class stList 005. { 006. private: 007. //+----------------+ 008. T info; 009. stList <T> *prev, 010. *start; 011. uint counter; 012. //+----------------+ 013. public: 014. //+----------------+ 015. stList(void) 016. :prev(NULL), 017. start(NULL), 018. counter(0) 019. {} 020. //+----------------+ 021. void Store(T arg, const uint index = 0xFFFFFFFF) 022. { 023. stList <T> *loc, 024. *ptr1 = start, 025. *ptr2 = NULL; 026. 027. for (uint c = 0; (ptr1 != NULL) && (c < index); ptr2 = ptr1, ptr1 = (*ptr1).start, c++); 028. 029. loc = new stList <T>; 030. (*loc).info = arg; 031. (*loc).start = (ptr2 != NULL ? (*ptr2).start : ptr1); 032. (*loc).prev = (ptr1 != NULL ? (*ptr1).prev : ptr2); 033. if (ptr2 != NULL) (*ptr2).start = loc; else start = loc; 034. if (ptr1 != NULL) (*ptr1).prev = loc; else prev = loc; 035. 036. counter++; 037. } 038. //+----------------+ 039. bool Restore(T &arg, const uint index = 0xFFFFFFFF) 040. { 041. if ((prev == NULL) || (start == NULL)) 042. return false; 043. 044. stList <T> *loc = (index < counter ? start : prev), 045. *ptr = NULL; 046. 047. for (uint c = 0; (loc != NULL) && (c < index) && (index < counter); ptr = loc, loc = (*loc).start, c++); 048. if (loc == NULL) return false; 049. 050. if (index == 0) 051. { 052. start = (*loc).start; 053. if (start != NULL) (*start).prev = NULL; 054. } else if (index >= (counter - 1)) 055. { 056. prev = (*loc).prev; 057. if (prev != NULL) (*prev).start = NULL; 058. } 059. else 060. { 061. (*ptr).start = (*loc).start; 062. (*loc).start.prev = ptr; 063. } 064. arg = (*loc).info; 065. delete loc; 066. counter--; 067. 068. return true; 069. } 070. //+----------------+ 071. // bool Exclude(const uint index) 072. // { 073. // T tmp; 074. 075. // return Restore(tmp, index); 076. // } 077. //+----------------+ 078. stList <T> *operator[](const uint arg) 079. { 080. stList <T> *loc = start; 081. 082. return loc; 083. } 084. //+----------------+ 085. void operator=(const T arg) 086. { 087. info = arg; 088. } 089. //+----------------+ 090. void Debug(void) 091. { 092. Print("===== DEBUG ====="); 093. for (stList <T> *loc = start; loc != NULL; loc = (*loc).start) 094. PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).start, loc, (*loc).prev, (*loc).info); 095. Print("================="); 096. } 097. //+----------------+ 098. }; 099. //+------------------------------------------------------------------+ 100. void OnStart(void) 101. { 102. stList <char> list; 103. 104. list.Store(10); 105. list.Store(84); 106. list.Store(-6); 107. list.Store(47, 0); 108. 109. list.Debug(); 110. 111. // list.Exclude(3); 112. list.Store(35, 2); 113. 114. list.Debug(); 115. 116. for (char info; list.Restore(info, 0);) 117. Print(info); 118. }; 119. //+------------------------------------------------------------------+
Código 08
Agora quando executamos este código 08, temos como resultado o que é visto logo abaixo.

Imagem 10
No entanto, preste atenção neste código 08, pois na linha 78 começamos a implementar o que será a sobrecarga do operador subscrito. Assim como também, na linha 85, já implementamos o que será a primeira sobrecarga do operador de atribuição. Porém apesar de termos feito isto, o código presente no procedimento OnStart, continua basicamente o mesmo. No entanto, o objetivo deste código 08 é justamente gerar a imagem 10. Assim, podemos fazer a próxima modificação. E esta tornará o procedimento OnStart ligeiramente diferente, como você pode observar logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. template <typename T> class stList 005. { 006. private: 007. //+----------------+ 008. T info; 009. stList <T> *prev, 010. *start; 011. uint counter; 012. //+----------------+ 013. public: 014. //+----------------+ 015. stList(void) 016. :prev(NULL), 017. start(NULL), 018. counter(0) 019. {} 020. //+----------------+ 021. void Store(T arg, const uint index = 0xFFFFFFFF) 022. { 023. stList <T> *loc, 024. *ptr1 = start, 025. *ptr2 = NULL; 026. 027. for (uint c = 0; (ptr1 != NULL) && (c < index); ptr2 = ptr1, ptr1 = (*ptr1).start, c++); 028. 029. loc = new stList <T>; 030. (*loc).info = arg; 031. (*loc).start = (ptr2 != NULL ? (*ptr2).start : ptr1); 032. (*loc).prev = (ptr1 != NULL ? (*ptr1).prev : ptr2); 033. if (ptr2 != NULL) (*ptr2).start = loc; else start = loc; 034. if (ptr1 != NULL) (*ptr1).prev = loc; else prev = loc; 035. 036. counter++; 037. } 038. //+----------------+ 039. bool Restore(T &arg, const uint index = 0xFFFFFFFF) 040. { 041. if ((prev == NULL) || (start == NULL)) 042. return false; 043. 044. stList <T> *loc = (index < counter ? start : prev), 045. *ptr = NULL; 046. 047. for (uint c = 0; (loc != NULL) && (c < index) && (index < counter); ptr = loc, loc = (*loc).start, c++); 048. if (loc == NULL) return false; 049. 050. if (index == 0) 051. { 052. start = (*loc).start; 053. if (start != NULL) (*start).prev = NULL; 054. } else if (index >= (counter - 1)) 055. { 056. prev = (*loc).prev; 057. if (prev != NULL) (*prev).start = NULL; 058. } 059. else 060. { 061. (*ptr).start = (*loc).start; 062. (*loc).start.prev = ptr; 063. } 064. arg = (*loc).info; 065. delete loc; 066. counter--; 067. 068. return true; 069. } 070. //+----------------+ 071. // bool Exclude(const uint index) 072. // { 073. // T tmp; 074. 075. // return Restore(tmp, index); 076. // } 077. //+----------------+ 078. stList <T> *operator[](const uint arg) 079. { 080. stList <T> *loc = start; 081. for (uint c = 0; (loc != NULL) && (c < arg); loc = (*loc).start, c++); 082. return loc; 083. } 084. //+----------------+ 085. void operator=(const T arg) 086. { 087. info = arg; 088. } 089. //+----------------+ 090. void Debug(void) 091. { 092. Print("===== DEBUG ====="); 093. for (stList <T> *loc = start; loc != NULL; loc = (*loc).start) 094. PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).start, loc, (*loc).prev, (*loc).info); 095. Print("================="); 096. } 097. //+----------------+ 098. }; 099. //+------------------------------------------------------------------+ 100. void OnStart(void) 101. { 102. stList <char> list; 103. 104. list.Store(10); 105. list.Store(84); 106. list.Store(-6); 107. list.Debug(); 108. list[0] = 47; 109. list.Debug(); 110. list[2] = 35; 111. 112. list.Debug(); 113. 114. for (char info; list.Restore(info, 0);) 115. Print(info); 116. }; 117. //+------------------------------------------------------------------+
Código 09
Agora quando você executar este código 09 no terminal do MetaTrader 5, o resultado que será visto é mostrado logo abaixo.

Imagem 11
Cara que negócio mais doido. Mas vejo que o resultado está sendo diferente do que o visto nas imagens anteriores. Será que não teria como conservar os resultados vistos anteriormente? Bem, não sei por que você iria querer fazer este tipo de coisa, meu caro leitor. Visto que isto acabaria por tornar a coisa muito mais complicada de ser entendida. Porém antes de mostrar como isto poderia ser feito. Quero que você observe o fato de que agora o próprio operador subscrito está criando uma maneira de referenciar a lista como se ela fosse um array. Coisa que antes não era possível de ser feita.
Mas vamos supor, e que isto fique bem claro, pois não estará no anexo, que você deseje manter o código com o mesmo comportamento, ou resultado que foi visto no código 08. E isto usando o código presente no procedimento OnStart visto no código 09. Como isto poderia ser feito? Bem, para fazer isto, precisamos mudar as coisas de uma maneira bem sutil. E esta é mostrada logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. template <typename T> class stList 005. { 006. private: 007. //+----------------+ 008. T info; 009. stList <T> *prev, 010. *start; 011. uint counter; 012. //+----------------+ 013. public: 014. //+----------------+ 015. stList(void) 016. :prev(NULL), 017. start(NULL), 018. counter(0) 019. {} 020. //+----------------+ 021. void Store(T arg, const uint index = 0xFFFFFFFF) 022. { 023. this[index > counter ? counter : index] = arg; 024. } 025. //+----------------+ 026. bool Restore(T &arg, const uint index = 0xFFFFFFFF) 027. { 028. if ((prev == NULL) || (start == NULL)) 029. return false; 030. 031. stList <T> *loc = (index < counter ? start : prev), 032. *ptr = NULL; 033. 034. for (uint c = 0; (loc != NULL) && (c < index) && (index < counter); ptr = loc, loc = (*loc).start, c++); 035. if (loc == NULL) return false; 036. 037. if (index == 0) 038. { 039. start = (*loc).start; 040. if (start != NULL) (*start).prev = NULL; 041. } else if (index >= (counter - 1)) 042. { 043. prev = (*loc).prev; 044. if (prev != NULL) (*prev).start = NULL; 045. } 046. else 047. { 048. (*ptr).start = (*loc).start; 049. (*loc).start.prev = ptr; 050. } 051. arg = (*loc).info; 052. delete loc; 053. counter--; 054. 055. return true; 056. } 057. //+----------------+ 058. // bool Exclude(const uint index) 059. // { 060. // T tmp; 061. 062. // return Restore(tmp, index); 063. // } 064. //+----------------+ 065. stList <T> *operator[](const uint arg) 066. { 067. stList <T> *loc, 068. *ptr1 = start, 069. *ptr2 = NULL; 070. 071. for (uint c = 0; (ptr1 != NULL) && (c < arg); ptr2 = ptr1, ptr1 = (*ptr1).start, c++); 072. 073. loc = new stList <T>; 074. (*loc).start = (ptr2 != NULL ? (*ptr2).start : ptr1); 075. (*loc).prev = (ptr1 != NULL ? (*ptr1).prev : ptr2); 076. if (ptr2 != NULL) (*ptr2).start = loc; else start = loc; 077. if (ptr1 != NULL) (*ptr1).prev = loc; else prev = loc; 078. 079. counter++; 080. 081. return loc; 082. } 083. //+----------------+ 084. void operator=(const T arg) 085. { 086. info = arg; 087. } 088. //+----------------+ 089. void Debug(void) 090. { 091. Print("===== DEBUG ====="); 092. for (stList <T> *loc = start; loc != NULL; loc = (*loc).start) 093. PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).start, loc, (*loc).prev, (*loc).info); 094. Print("================="); 095. } 096. //+----------------+ 097. }; 098. //+------------------------------------------------------------------+ 099. void OnStart(void) 100. { 101. stList <char> list; 102. 103. list.Store(10); 104. list.Store(84); 105. list.Store(-6); 106. list.Debug(); 107. list[0] = 47; 108. list.Debug(); 109. list[2] = 35; 110. 111. list.Debug(); 112. 113. for (char info; list.Restore(info, 0);) 114. Print(info); 115. }; 116. //+------------------------------------------------------------------+
Código 10
Ao usar este código 10, lembrando que ele NÃO ESTARÁ NO ANEXO, devido a motivos óbvios. Você irá conseguir produzir o resultado que é visto na imagem 12 logo abaixo.

Imagem 12
Note que boa parte desta imagem 12, se parece em muito com o que podemos ver na imagem 10. Mostrando que de fato o código 10, conseguir gerar o mesmo resultado do código 08. Porém fazendo uso da sobrecarga de operadores. Mas preste atenção ao seguinte fato neste código 10. Se você olhar dentro da classe, irá perceber que o conteúdo que estava presente no procedimento Store, agora está dentro do que é a sobrecarga do operador subscrito. E que o tal procedimento Store, está criando uma referência a fim de acessar justamente o operador sobrecarregado. Ao ver isto, você pode pensar o seguinte: Não precisamos mais deste procedimento Store. Podemos de maneira pura e simples usar o que é visto no fragmento logo abaixo, e ainda assim a lista seria criada com o mesmo resultado mostrado na imagem 12, logo acima.
. . . 098. //+------------------------------------------------------------------+ 099. void OnStart(void) 100. { 101. stList <char> list; 102. 103. list[0] = 10; 104. list[1] = 84; 105. list[2] = -6; 106. list.Debug(); 107. list[0] = 47; 108. list.Debug(); 109. list[2] = 35; 110. 111. list.Debug(); 112. 113. for (char info; list.Restore(info, 0);) 114. Print(info); 115. }; 116. //+------------------------------------------------------------------+
Fragmento 04
Sim meu caro leitor, você poderia usar isto que é mostrado no fragmento 04. Porém, quero lembrar a você, que o objetivo da sobrecarga de operadores, é justamente o de tornar o código mais simples e legível. No entanto, NÃO É ISTO que está acontecendo aqui. Visto que ao observar este fragmento 04, e muitas vezes a classe de fato estará em um arquivo de cabeçalho. Você é levado ao erro de imaginar que deveria haver apenas e somente três valores dentro do que seria a lista encadeada. E não cinco, como podemos ver ao executar o código.
E o que ao meu entender e na visão de muitos outros programadores é a parte ainda mais confusa. Ao olhar apenas e somente este fragmento 04, você pode imaginar que a lista começa com o valor 10 e termina no valor -6, e que quando as linhas 107 e 109 forem executadas, estes valores serão substituídos pelos valores 47 e 35, respectivamente. Isto é o tal problema que sempre devemos tomar cuidado ao implementar a sobrecarga de operadores.
E é justamente por conta disto que este código 10, NÃO ESTARÁ DISPONIVEL NO ANEXO. Sendo o mesmo apenas uma mera curiosidade aqui neste e para este artigo.
Um último detalhe antes de finalizar o artigo
Muito bem, muito bom. Tudo é muito lindo e maravilhoso. No entanto, existe uma pequena limitação que torna as coisas um pouco complicadas quando o assunto é sobrecarga do operador subscrito. E isto quando o assunto está relacionado ao MQL5. Pelo menos até o devido momento em que escrevo este artigo, não consegui encontrar como contornar tal limitação.
E esta limitação é um tanto quanto triste, se é que podemos vir a definir as coisas desta maneira. O fato é que aqui no MQL5, NÃO TEMOS como utilizar ponteiros, da mesma maneira que usamos em linguagens como C ou mesmo em C++. E o fato de isto ocorrer, dificulta alguns pontos chaves quando o assunto é sobrecarga de operadores. Sei que muitos podem não entender o que irei falar agora. Mas quem já programa a um pouco mais de tempo irá entender perfeitamente bem.
Quando fazemos a sobrecarga de um operador subscrito, o tal [], NÃO RETORNAMOS as coisas com foram feitas aqui neste artigo. No caso, sempre retornamos uma referência a posição de memória onde o dado a ser modificado se encontra. Salvo casos mais complexos, em que de fato fazemos uso de algo muito parecido com o que foi visto aqui.
Naturalmente, quando o código 03 foi mostrado, muitos já devem ter ficado bastante interessados. Assim como quando o código 06 foi demonstrado. Mas com toda a certeza, a empolgação deve ter atingido seu clímax, no código 09 ou quem sabe no código 10. No entanto, não sei que vocês perceberam. Mas em todos os casos, estamos utilizando a sobrecarga para escrever e em nenhum momento para ler. Por que? O motivo é justamente a tal limitação da qual mencionei que existe no MQL5, pelo fato de não podermos usar ponteiros como no C ou C++.
Para exemplificar, vou mostrar como seria um código muito simples em C, que faria uso da sobrecarga. Este pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #include <stdio.h> 03. //+------------------------------------------------------------------+ 04. template <typename T> class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. T *u8; 10. //+----------------+ 11. public : 12. //+----------------+ 13. stDemo(const int arg) 14. { 15. u8 = new T[arg]; 16. } 17. //+----------------+ 18. T &operator[](const int arg) 19. { 20. return u8[arg]; 21. } 22. //+----------------+ 23. }; 24. //+------------------------------------------------------------------+ 25. int main(void) 26. { 27. stDemo <char> demo(8); 28. char v1; 29. 30. demo[3] = 10; 31. demo[1] = demo[3]; 32. v1 = demo[1]; 33. 34. printf("%d", v1); 35. 36. return 0; 37. } 38. //+------------------------------------------------------------------+
Código 11
Aqui neste código 11 temos uma sobrecarga totalmente funcional. No entanto este código NÃO É MQL5. Ele é C++. E o que quero chamar a sua atenção é justamente para as linhas 31, mas principalmente para a linha 32. Note que nesta linha 32 estamos lendo um valor e o colocado em uma variável discreta. Que nada tem a ver com a classe. Porém no MQL5 não podemos fazer isto. Pelo menos não desta maneira.
No MQL5, você precisa decidir se vai querer gravar ou ler dados, discretos, usando o operador subscrito. No entanto é mais adequado, como foi visto neste artigo que venhamos a gravar dados. Já que no caso da leitura teríamos muito mais limitações. E a limitação de leitura de dados discretos, pode ser contornada fazendo uso de uma função, ou procedimento voltados apenas a esta questão. Quando a sobrecarga do operador subscrito estiver voltada para gravar dados.
Cara você gosta de ser estraga prazer. Eu estava todo feliz, contente e satisfeito. Aí vem você e acaba com a minha alegria. Mas tudo bem, como não sou tão experiente assim, não posso me queixar da sua disposição em explicar certos detalhes. Mas somente lhe darei o meu perdão, se você me mostrar como poderíamos fazer em MQL5, algo parecido com o que é feito por este código 11 escrito em C++.
Ok, o código em MQL5, que consegue produzir o mesmo resultado deste código C++ visto em código 11 é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. template <typename T> class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. T u8[]; 10. int m_index; 11. //+----------------+ 12. public : 13. //+----------------+ 14. stDemo(const int arg) 15. { 16. m_index = -1; 17. ArrayResize(u8, arg); 18. } 19. //+----------------+ 20. stDemo <T> *operator[](const int arg) 21. { 22. m_index = arg; 23. return &this; 24. } 25. //+----------------+ 26. void operator=(const T arg) 27. { 28. u8[m_index - 1] = arg; 29. } 30. //+----------------+ 31. const T GetInfo(void) const 32. { 33. return u8[m_index - 1]; 34. } 35. //+----------------+ 36. }; 37. //+------------------------------------------------------------------+ 38. void OnStart(void) 39. { 40. stDemo <char> demo(8); 41. char v1; 42. 43. demo[3] = 10; 44. demo[1] = demo[3].GetInfo(); 45. v1 = demo[1].GetInfo(); 46. 47. Print(v1); 48. } 49. //+------------------------------------------------------------------+
Código 12
Agora preste atenção. Neste código 12, que está escrito em MQL5. Temos a sobrecarga do operador, visando justamente a escrita de dados dentro da estrutura da classe. Por conta deste fato, podemos tanto ler quanto também gravar as coisas sem muito custo. Desde é claro você faça uso da função vista na linha 31. Porém note que apesar de este código 12 dar o mesmo resultado que pode ser visto no código 11, escrito em C++. Ele é completamente diferente deste outro que podemos ver logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. template <typename T> class stDemo 05. { 06. //+----------------+ 07. private : 08. //+----------------+ 09. T u8[]; 10. //+----------------+ 11. public : 12. //+----------------+ 13. stDemo(const int arg) 14. { 15. ArrayResize(u8, arg); 16. } 17. //+----------------+ 18. stDemo <T> *Set(const int arg, const T info) 19. { 20. u8[arg - 1] = info; 21. return &this; 22. } 23. //+----------------+ 24. T operator[](const int arg) 25. { 26. return u8[arg - 1]; 27. } 28. //+----------------+ 29. }; 30. //+------------------------------------------------------------------+ 31. void OnStart(void) 32. { 33. stDemo <char> demo(8); 34. char v1; 35. 36. demo.Set(3, 10); 37. demo.Set(1, demo[3]); 38. v1 = demo[1]; 39. 40. Print(v1); 41. } 42. //+------------------------------------------------------------------+
Código 13
Neste caso do código 13, que também está escrito em MQL5. Temos a sobrecarga do operador subscrito, visando justamente a leitura de dados de dentro da estrutura da classe. Note que está simples mudança, faz total e completa diferença na forma como você irá trabalhar ou deverá implementar o seu código. O resultado de todos estes três códigos é exatamente o seguinte: No terminal, seja no console, prompt de comando, ou mesmo no MetaTrader 5, iremos ver o valor dez sendo impresso. E isto em ambos os códigos.
Porém você pode estar olhando para este código 12 ou mesmo para o código 13 e pensando: Será que não teria como unir ambas as coisas? Ou melhor ainda. Será que não poderia criar algo parecido com o que é visto no código 11, escrito em C++? Bem, meu caro leitor, infelizmente a resposta para esta pergunta é NÃO. Pelo menos até onde consigo pensar em uma forma de abordar a sobrecarga diretamente no MQL5. Existe um outro jeito, mas envolve justamente unir códigos escritos em C++ com código escrito em MQL5. Porém isto é material que ao meu ver é avançado. Coisa que não iremos abordar aqui nesta sequência de artigos. Quem sabe em uma futura sequência, visando tratar apenas de material avançado.
Considerações finais
Neste artigo fizemos uma primeira abordagem a fim de trabalhar e demonstrar como poderíamos implementar a sobrecarga do operador subscrito e também do operador de atribuição. Tentando com isto trazer uma abordagem prática e que fosse interessante para todos. Porém o que foi visto aqui, é apenas uma parte daquilo que pretendo ainda mostrar e que está diretamente ligado a sobrecarga de tais operadores. Visto que neste artigo, tivemos a presença do ilustríssimo código 07, e que não foi demonstrado como poderíamos obter o mesmo resultado utilizando a sobrecarga de operadores. Iremos no próximo artigo, focarmos apenas e tão somente neste único ponto. Isto para que você possa ver, que se bem pensado, podemos criar um código inteiro, usando a sobrecarga de operadores sem muita dificuldade. E no final, o código irá ficar muito mais legível. Pelo menos é assim que eu vejo. Já que acho bem mais simples entender o código que será visto no próximo artigo, do que este código 07 visto neste artigo daqui.
De qualquer maneira, procure estudar e praticar o que foi visto aqui. E tendo interesse em continuar aprendendo, não se esqueça. Em breve irá sair o próximo artigo. Então até 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 |
| Code 06 | Demonstração básica |
| Code.cpp | 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 (III)
Algoritmo de otimização caótica — Chaos optimization algorithm (COA): Continuação
Implementação do mecanismo de breakeven em MQL5 (Parte 1): Classe base e modo de breakeven por pontos fixos
Trading de arbitragem no Forex: sistema de negociação matricial para retorno ao valor justo com limitação de risco
- 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