Do básico ao intermediário: Classes (II)
Introdução
No artigo anterior Do básico ao intermediário: Classes (I), começamos a introduzir o assunto referente a programação orientada em objetos. Porém naquele artigo, apenas falamos sobre constructores de classes. Mas as vezes precisamos implementar uma outra chamada especial, que existe apenas quando utilizamos classes, que é justamente os destructores.
Para compreender adequadamente como os destructores funcionam, podemos fazer um tipo de paralelo entre a programação orientada em objetos, que de fato criam e utilizam as classes. E uma programação baseada em eventos, presente e vista no MetaTrader 5. Hã! Que coisa bizarra e mais estranha é está que você pretende criar? Calma meu caro leitor. Calma, você logo irá entender. E garanto que você irá conseguir assimilar muito melhor, porque destructores existem, e como você os deve implementar. Isto quando for preciso implementar os mesmos.
Então chegou a hora de dar uma pausa em qualquer coisa que possa lhe distrair e focar no que será visto neste artigo. Dado este aviso, podemos partir para o próximo tópico.
Classes (II)
Um dos conceitos mais difíceis de aprender, isto quando o assunto é programação orientada em objetos, é o conceito de destructor. Isto por que, na grande maioria dos casos, eles simplesmente não fazem o menor sentido. E entendo em vista, o fato de que, você como programador, de maneira alguma terá como chamar um destructor, isto de maneira explicita, torna ainda mais complicado entender, por que destructores existem. O que acaba não ajudando em nada, a um jovem programador entender o próprio conceito de destructor.
Programadores da velha guarda, assim como eu, que literalmente viram a programação se desenvolver ao longo dos anos, já tivemos algumas dificuldades em entender alguns conceitos e mecanismos que foram criados aos longos dos anos. Agora pense em um programador, que já pega todos estes conceitos já implementados, e os precisa entender. Santa confusão. Ainda mais que muitas das vezes, muitos destes conceitos, são muito mal explicados e acabam tornando algo que a princípio é algo muito simples, em um monstro estilo hidra, que quanto mais você corta as cabeças a fim de o matar, mais complicado a coisa vai se tornando. E um destes conceitos é justamente o destructor. Que você neste artigo irá entender que apesar de parecer complicado, é muito simples. E entendendo adequadamente o princípio de funcionamento do destructor, será muito mais fácil de você entender quando e como implementar o destructor em suas classes.
Muito bem, para começar, vamos esquecer por um instante a programação orientada em objetos. E vamos partir para algo um pouco mais simples e prático. Que seria a programação orientada a eventos. Este tipo de programação é justamente o que utilizamos quando criamos algo a ser utilizado no MetaTrader 5. Isto quando o assunto são indicadores e Expert Advisores. Ok, mas o que programação orientada a eventos, tem a ver com programação orientada em objetos? Para ser sincero, meu caro leitor, não tem muita coisa a ver. Porém, por estamos utilizando o MQL5, que visa justamente permitir a um programador tomar o controle sobre como o MetaTrader 5 deverá funcionar. Torna sim, muito simples criar um tipo de ligação, entre o que seria uma programação orientada a eventos e uma orientada em objetos. Isto a fim de demonstrar como um destructor funciona na prática.
Para começar, vamos iniciar um indicador bem simples e básico. Este pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo" 04. //+------------------------------------------------------------------+ 05. int OnInit() 06. { 07. return INIT_SUCCEEDED; 08. }; 09. //+------------------------------------------------------------------+ 10. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 11. { 12. return rates_total; 13. }; 14. //+------------------------------------------------------------------+ 15. void OnDeinit(const int reason) 16. { 17. }; 18. //+------------------------------------------------------------------+
Código 01
Como foi visto, nos primeiros artigos sobre indicadores, nesta mesma sequência, indicadores, assim como o Expert Advisores, trabalham com eventos. Sendo estes fornecidos e disparados pelo MetaTrader 5 na maior parte das vezes. Apesar disto, existem momentos em que eventos podem ser disparados pelo código. Mas isto não vem ao caso. O importante é entender que os eventos são oriundos do MetaTrader 5.
Para mais detalhes, procure ver os artigos anteriores, mas basicamente este Do básico ao intermediário: Eventos (I), irá lhe ajudar bastante a entender algumas das coisas que você precisa saber, para conseguir acompanhar este artigo daqui.
Ou seja, este código 01, pode ser pensado como sendo uma classe inteira. Mas como assim? Não entendi esta sua colocação. Calma, você já vai entender. Lembra que no artigo anterior falamos sobre o fato de que existiam duas chamadas especiais dentro de uma classe? Uma que seria o constructor, que tem como objetivo inicializar a classe e outra que seria o destructor, que teria como objetivo destruir o que foi construído pela classe? Pois bem, olhe novamente para este código 01. Tendo em vista que se trata de um indicador, quando o MetaTrader 5 o colocar no gráfico, irá chamar e executar, primeiramente o código contido na função OnInit. E que quando removermos este mesmo indicador do gráfico, o MetaTrader 5, irá chamar e executar o procedimento OnDeinit. Isto caso o tal procedimento exista. Isto a fim de que qualquer coisa que desejarmos remover, seja removida do gráfico. Devido justamente ao fato de que o indicador já não se encontra mais no gráfico.
Com base neste conceito muito simples, e esta similaridade que existe entre a programação orientada em objetos e a forma como este código do indicador trabalha. Podemos implementar algo dentro deste código 01. E assim começar a entender como um destructor, de fato trabalha.
Assim sendo, vamos adicionar algum objeto no gráfico e quando o indicador for removido, remover também o objeto. Fazer isto é algo muito simples e já foi explicado em outros artigos aqui nesta mesma sequência.
Como base no que iremos fazer, tomaremos o artigo Do básico ao intermediário: Objetos (I) como base de apoio. Neste artigo, mostrei como adicionar e remover algum objeto no gráfico. Então modificando o código 01, para utilizar algo visto no artigo mencionado, temos agora o código mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo" 04. //+------------------------------------------------------------------+ 05. #define def_NameChannel "Demo" 06. //+------------------------------------------------------------------+ 07. int OnInit() 08. { 09. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, 0, 0); 10. 11. return INIT_SUCCEEDED; 12. }; 13. //+------------------------------------------------------------------+ 14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 15. { 16. return rates_total; 17. }; 18. //+------------------------------------------------------------------+ 19. void OnDeinit(const int reason) 20. { 21. ObjectDelete(0, def_NameChannel); 22. ChartRedraw(); 23. }; 24. //+------------------------------------------------------------------+
Código 02
Este código 02, tem como objetivo criar um objeto do tipo OBJ_REGRESSION e o apresentar no gráfico. E assim que este indicador for removido do gráfico, o objeto será removido junto. Porém, ao executar este código, você irá notar que nenhum objeto irá ser apresentado no gráfico. Apesar de ter sido criado devido a execução da linha nove. Então como podemos resolver isto? É muito simples meu caro leitor, basta modificar o código como mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo" 04. //+------------------------------------------------------------------+ 05. #define def_NameChannel "Demo" 06. //+------------------------------------------------------------------+ 07. int OnInit() 08. { 09. datetime dt0 = TimeCurrent(), 10. dt1[20]; 11. 12. CopyTime(NULL, NULL, dt0, dt1.Size(), dt1); 13. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, dt1[0], 0, dt0, 0); 14. 15. return INIT_SUCCEEDED; 16. }; 17. //+------------------------------------------------------------------+ 18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 19. { 20. return rates_total; 21. }; 22. //+------------------------------------------------------------------+ 23. void OnDeinit(const int reason) 24. { 25. ObjectDelete(0, def_NameChannel); 26. ChartRedraw(); 27. }; 28. //+------------------------------------------------------------------+
Código 03
A mudança que foi preciso ser feita, foi apenas a de capturar as datas que queremos utilizar para gerar posicionar o objeto de regressão. Isto é feito pelas linhas nove e doze. Assim o resultado é o que pode ser visto logo abaixo.

Imagem 01
Agora preste atenção ao seguinte fato. Quando adicionamos este indicador no gráfico, temos o objeto definido na linha 13 sendo criado e posicionado. E quando removemos este indicador do gráfico, este mesmo objeto, criado na linha treze é removido do gráfico, graças a linha 25. Ou seja, o tratador de evento OnInit, está fazendo a vez do constructor e o tratador de evento OnDeinit está fazendo a vez do destructor. Ok, mas o que isto tem a ver com programação orientada em objetos? Ainda não consegui entender onde você está querendo chegar.
Bem meu caro leitor, agora você irá entender, pois chegou à parte divertida. Que é justamente o de transformar este mesmo código 03 em um programa que fará uso da programação orientada em objetos. Então redobre a sua atenção para entender como isto será feito. Como este código 03 é um indicador, e para utilizar a programação orientada em objetos, neste mesmo código é preciso usar dois comandos que ainda não foram explicados. Vamos mudar o foco agora para transformar este mesmo código 03 em um script. Isto é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. datetime dt0 = TimeCurrent(), 09. dt1[20]; 10. 11. CopyTime(NULL, NULL, dt0, dt1.Size(), dt1); 12. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, dt1[0], 0, dt0, 0); 13. ChartRedraw(); 14. 15. Sleep(2000); 16. 17. ObjectDelete(0, def_NameChannel); 18. ChartRedraw(); 19. } 20. //+------------------------------------------------------------------+
Código 04
Note que agora estamos usando um script no lugar do indicador. Porém isto não muda o fato do que estamos ainda fazendo. Apenas direciona tudo para um modelo onde será mais simples explicar e visualizar a execução do constructor e destructor da classe que ainda iremos criar. Agora para que o objeto criado, não desapareça assim que ele for criado, usamos a linha quinze para permitir um pequeno delay entre a colocação e a remoção do objeto. Isto fará com que o código ao ser executado, trabalhe como mostrado na animação logo abaixo.

Animação 01
Legal, script funcionando, perfeitamente bem, agora vamos a diversão. Assim tudo que precisamos fazer é modificar este mesmo código 04 para o código visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. class C_Regression 07. { 08. private : 09. //+----------------+ 10. public : 11. //+----------------+ 12. C_Regression() 13. { 14. datetime dt0 = TimeCurrent(), 15. dt1[20]; 16. 17. CopyTime(NULL, NULL, dt0, dt1.Size(), dt1); 18. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, dt1[0], 0, dt0, 0); 19. ChartRedraw(); 20. } 21. //+----------------+ 22. }; 23. //+------------------------------------------------------------------+ 24. void OnStart(void) 25. { 26. C_Regression channel; 27. 28. Sleep(2000); 29. 30. ObjectDelete(0, def_NameChannel); 31. ChartRedraw(); 32. } 33. //+------------------------------------------------------------------+
Código 05
Agora preste muita atenção, meu caro leitor. Este código 05, irá produzir o mesmo resultado visto na animação 01. Porém aqui, começamos a ver como isto seria feito, usando a programação orientada em objetos. Contando com o fato de que, esta parte referente ao constructor, foi explicada no artigo anterior, podemos partir para o que seria o destructor. E é agora que muito iniciante fica completamente abobalhado por não entender como as coisas funcionam. Isto justamente devido ao fato de que muitos autores não explicam as coisas como estou tentando fazer aqui.
Assim sendo, para gerar o destructor neste mesmo código 05, a fim de tornar a programação perfeitamente dentro dos princípios da programação orientada em objetos. Apenas modificamos este mesmo código 05, para o código que pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. class C_Regression 07. { 08. private : 09. //+----------------+ 10. public : 11. //+----------------+ 12. C_Regression() 13. { 14. datetime dt0 = TimeCurrent(), 15. dt1[20]; 16. 17. CopyTime(NULL, NULL, dt0, dt1.Size(), dt1); 18. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, dt1[0], 0, dt0, 0); 19. ChartRedraw(); 20. } 21. //+----------------+ 22. ~C_Regression() 23. { 24. ObjectDelete(0, def_NameChannel); 25. ChartRedraw(); 26. } 27. //+----------------+ 28. }; 29. //+------------------------------------------------------------------+ 30. void OnStart(void) 31. { 32. C_Regression channel; 33. 34. Sleep(2000); 35. } 36. //+------------------------------------------------------------------+
Código 06
Agora execute este código 06 no terminal MetaTrader 5, e se surpreenda com o resultado que você poderá ver meu caro leitor. Você irá notar que ele, será o mesmo que podemos ver na animação 01. Mas por que? Lembre-se do seguinte fato. O MetaTrader 5 apenas irá executar o código que estiver dentro do procedimento OnStart. Mas olhando para dentro deste procedimento, podemos ver apenas duas linhas sendo declaradas. E mesmo assim o código manteve seu comportamento. Ou seja, ele cria o objeto, o coloca no gráfico, deixa passar algum tempo e logo depois remove este objeto do gráfico. Então novamente a pergunta é como isto aconteceu? E a resposta é por que o destructor está sendo chamada.
Mas como assim? Não estou conseguindo ver absolutamente nenhum tipo de chamada, além das duas linhas presentes dentro do procedimento OnStart. Tudo que estou conseguindo ver de diferente neste código 06, é que você criou um outro procedimento, dentro da classe e com o mesmo nome da classe. Mas antes do nome, adicionou um símbolo gráfico. E olhando com calma este procedimento adicionado, consigo notar que dentro dele foi colocado o código que estava nas linhas 30 e 31 do código 05. A nada mais foi adicionado ao código. E no entanto você diz e posso ver ao executar o código, que o objeto de fato está sendo removido. Agora que não estou entendendo mais nada. Isto não faz o mínimo de sentido para mim. Já sei, isto só pode ser coisa do DEMO. Por que DEUS não faria algo tão complicado assim.
De fato meu caro leitor. Destructores são um tanto quanto engraçados. E para os entender de maneira adequada, precisamos entender um outro conceito. Este conceito que precisamos saber, foi explicado lá nos primeiros artigos desta mesma sequência, quando falamos de variáveis. Mas preste atenção e você irá entender por que este código 06 funciona. E como você deve fazer para declarar e utilizar um destructor.
Porém, no artigo Do básico ao intermediário: Variáveis (I), falamos sobre o tempo de vida e visibilidade das variáveis. Caso tenha alguma dúvida a este respeito, leia o artigo mencionado para maiores esclarecimentos. Pois compreender estes dois tópicos, que seria o tempo de vida e a visibilidade de uma variável, irá lhe ajudar a entender quando um destructor é chamado pelo compilador. Assim como também lhe ajudará a tirar mais proveito da programação orientada em objetos.
Muito bem, então vamos por partes, como diria Jack. Primeiro, quando for declarar um destructor, você precisa fazer isto, da mesma maneira que está sendo mostrado na linha 22 do código 06. Ou seja, você irá adicionar o símbolo ( ~ ) antes do nome da classe. Isto permitirá ao compilador saber que aquele procedimento específico que estamos declarando, trata-se de um destructor da própria classe. Como tanto o destructor, quanto o constructor NUNCA RETORNAM NENHUM VALOR. Você não pode utilizar eles para este fim. Você precisa confiar que seu código estará fazendo tudo da melhor maneira possível.
Segundo ponto: Diferente dos constructores, que podem receber algum argumento de inicialização, como foi mostrado no artigo anterior. Destructores NUNCA, JAMAIS e em HIPOTESE ALGUMA, irão receber algum argumento. Tentar fazer isto é um erro, e isto irá impedir que seu código venha a ser compilado, gerando assim um executável.
Terceiro: Destructores são sempre chamados de maneira implícita pelo seu código. Salvo o fato de que venhamos a utilizar dois operadores que veremos em breve. Ou seja, em teoria, VOCÊ NÃO CONTROLA, quando e onde um destructor será utilizado. Quem faz isto é o compilador, com base no tempo de vida de uma variável. Novamente isto na teoria. Pois na prática, existem meios de controlar quando e onde o destructor será chamado. Mas por hora, vamos ficar focados na parte que é mais simples de entender.
Ok, agora vem a pergunta que raio de coisa é este de tempo de vida de uma variável? Pois bem, meu caro leitor, no primeiro artigo desta série, falamos sobre isto. Mas podemos dar uma rápida pincelada a este respeito aqui.
Uma variável nasce no momento de sua declaração e morre no final do bloco onde ela foi declarada.
Pois bem, dizer isto não me ajudou em nada. Pois continuo sem entender. Certo, se este foi o seu caso, sugiro que você volte nos primeiros artigos desta sequência e os estude. Pois ter os conceitos corretos irá lhe ajudar a tirar o máximo que podemos do MQL5. Sem isto, esquece, você vai ficar boiando na explicação.
Continuando, quando declaramos na linha 32 a variável channel, cujo tipo é a classe C_Regression. Estaremos chamando o constructor default da classe. Isto foi visto no artigo anterior. Porém, no momento em que o bloco de código, que se inicia na linha 31, é finalizado, e isto na linha 35. Temos a destruição das variáveis daquele mesmo bloco. No caso a única variável presente no bloco, é a variável channel. E como o compilador sabe, que a variável channel, representa uma classe. O compilador irá procurar dentro da classe C_Regression, o destructor. Caso não encontre um, o compilador irá providenciar algum, apenas para que a classe seja destruída de maneira adequada. Lembre-se do seguinte: O compilador NÃO SABE COMO DESTRUIR A CLASSE. Ele apenas providenciará um destructor que lhe pareça adequado. Com isto ele irá conseguir compilar o código gerando assim o executável.
No entanto, como na nossa classe C_Regression, definimos e implementamos um destructor, que se encontra implementado na linha 22. O compilador irá criar uma chamada, assim que a variável channel vier a ser destruída, para que este código implementado na linha 22 seja executado. E por conta disto, que mesmo sem nada sendo indicado de forma explicita no código. Temos a remoção do objeto OBJ_REGRESSION, do gráfico.
Que coisa mais legal. Agora sim comecei a entender como este código 06 funciona e como o destructor é chamado. Mas agora me bateu um certo incomodo. Venho acompanhando estes artigos, e tenho conseguido aprender muita coisa bem bacana e de maneira muito simplificada. No entanto, isto que foi mostrado aqui, aparentemente se aplica somente a questão mostrada aqui. Será que não podemos fazer as coisas de uma forma um pouco diferente? Como assim, meu caro leitor? Tente ser um pouco mais específico para que eu possa lhe ajudar.
Bem, estou querendo saber o seguinte: Neste código 06, estamos utilizando um script para demonstrar algo que foi visto quando utilizamos um indicador, que foi construído no código 03. Até neste ponto tudo bem. No caso do indicador, quando dizíamos ao MetaTrader 5 para o remover, era chamado o procedimento OnDeinit. Que no caso deste código 06, representaria a chamada do destructor, visto na linha 22, como foi explicado no texto do artigo. A mesma coisa se aplica ao constructor, quando no código 03, quem fazia este trabalho era a função OnInit, presente na linha sete. No caso do código 06, quem faz este trabalho é a linha doze, que é chamada quando a linha 32 é executada. A minha dúvida é justamente esta. Poderíamos usar esta mesma classe, que é vista no código 06, dentro do código 03? E se pudermos fazer isto, como isto deveria ser feito?
Excelente questão esta que você levantou, meu caro leitor. E a resposta para isto é SIM. Podemos utilizar esta mesma classe, vista e implementada no código 06, dentro do que seria o código 03. E sem nenhum problema. Para tornar isto possível e que você entenda perfeitamente bem como isto pode ser feito. Vamos transplantar esta mesma classe, para dentro de um arquivo de cabeçalho. Assim, podemos tanto usar ela no script, como também em um outro código qualquer. Seja ele um indicador, Expert Advisor, ou até mesmo um serviço. Se bem, que no caso de utilizar serviços, precisaremos fazer algumas outras coisas. Que ficaram para serem explicadas em um outro momento.
Com isto em mente, temos a criação do seguinte arquivo de cabeçalho, visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. class C_Regression 03. { 04. private : 05. //+----------------+ 06. public : 07. //+----------------+ 08. C_Regression() 09. { 10. datetime dt0 = TimeCurrent(), 11. dt1[20]; 12. 13. CopyTime(NULL, NULL, dt0, dt1.Size(), dt1); 14. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, dt1[0], 0, dt0, 0); 15. ChartRedraw(); 16. } 17. //+----------------+ 18. ~C_Regression() 19. { 20. ObjectDelete(0, def_NameChannel); 21. ChartRedraw(); 22. } 23. //+----------------+ 24. }; 25. //+------------------------------------------------------------------+
Código 07
Note que o código da classe foi mantido sem nenhuma modificação. Agora, para usar este mesmo código dentro de um script, como o visto em código 06, faremos algo um pouco diferente. Neste caso o novo código é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. #include <Tutorial\File 01.mqh> 07. //+------------------------------------------------------------------+ 08. void OnStart(void) 09. { 10. C_Regression channel; 11. 12. Sleep(2000); 13. } 14. //+------------------------------------------------------------------+
Código 08
Agora caso você não esteja entendo este código 08, sugiro ver o artigo Do básico ao intermediário: Diretiva Include, onde é explicado como a diretiva include funciona. Assim como alguns cuidados que precisamos tomar, para que o código funcione corretamente. E sem se esquecer que você precisará também entender como definições funcionam. Para este propósito, você pode ler o artigo Do básico ao intermediário: Definições (I), onde foi explicado o necessário para você entender completamente este código 08.
Ok, agora temos um arquivo de cabeçalho, que é visto no código 07, e que pode ser utilizado em outros tipos de código MQL5. Então vamos ver como utilizar esta mesma classe, só que agora em um código equivalente ao o que seria o código 03. Para tal objetivo, precisamos criar algo parecido com o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo" 04. //+------------------------------------------------------------------+ 05. #define def_NameChannel "Demo" 06. //+------------------------------------------------------------------+ 07. #include <Tutorial\File 01.mqh> 08. //+------------------------------------------------------------------+ 09. int OnInit() 10. { 11. return INIT_SUCCEEDED; 12. }; 13. //+------------------------------------------------------------------+ 14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 15. { 16. return rates_total; 17. }; 18. //+------------------------------------------------------------------+ 19. void OnDeinit(const int reason) 20. { 21. }; 22. //+------------------------------------------------------------------+
Código 09
Agora para que a classe seja corretamente utilizada aqui, neste código 09, temos que voltar a questão sobre variáveis. Novamente, sem entender conceitos mais simples, você não irá entender coisas mais elaboradas. Então se estiver com alguma dúvida, volte e procure estudar os artigos anteriores. Pois o nosso problema aqui, é o tempo de vida de uma variável. É sempre ele que as vezes dificulta um pouco as coisas.
No caso do código 03, não existia tal preocupação. Isto por que, não precisávamos de fato de alguma variável sendo definida no código. Tudo era feito de maneira local e totalmente isolada. Porém aqui neste novo código, não poderemos agir daquela maneira. Isto porque, se tentarmos fazer isto, teremos o problema a destruição da variável local, assim que o bloco de código for finalizado. Mas então, como resolver esta questão? Bem, o jeito é utilizar uma variável de escopo global. Assim ela não será destruída a menos que o código seja finalizado. E é neste momento que as coisas realmente ficam muito mais interessantes e divertidas. Mas ao mesmo tempo, também muito mais perigosas, para quem não se atenta ao que está sendo implementado. Ou tenta queimar etapas do aprendizado. Isto por que, o código que irá produzir o mesmo tipo de resultado visto até aqui, é mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo" 04. //+------------------------------------------------------------------+ 05. #define def_NameChannel "Demo" 06. //+------------------------------------------------------------------+ 07. #include <Tutorial\File 01.mqh> 08. //+------------------------------------------------------------------+ 09. C_Regression gl_Channel; 10. //+------------------------------------------------------------------+ 11. int OnInit() 12. { 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+ 21. void OnDeinit(const int reason) 22. { 23. }; 24. //+------------------------------------------------------------------+
Código 10
Este código 10 é muito doido, se você parar e pensar com calma. Isto pelo simples motivo de que em nenhum momento estamos criando nada no código. Apenas foi preciso definir na linha nove, a variável que irá utilizar a classe que está sendo incluída, graças a linha sete. Você pode até estar imaginando: Cara este código não irá fazer absolutamente nada. Já que dentro da chamada OnInit, não existe nenhum tipo de coisa sendo feita. Assim como nos demais tratadores. Como podemos observar, ao analisar o código em OnCalculate e em OnDeinit. Resumindo: Este código é completamente inútil. E tenho dito.
Hum. Acho que você não entendeu muito bem como o compilador faz para que as classes venham a trabalhar. Por isto você está dizendo que este código 10 é completamente inútil. No entanto, diferente do código 09, que não faz, de fato, absolutamente nada. O mesmo não pode ser dito com relação a este código 10. Este sim faz alguma coisa. Que é justamente o de colocar o objeto que está sendo definido dentro da classe C_Regression. Mas por que isto acontece? Já que aparentemente não existe nenhum trecho de código dentro das funções e procedimentos que serão executados pelo MetaTrader 5? De fato, se isto funciona, não consigo compreender o motivo. Poderia, por favor me esclarecer isto, para que eu também possa compreender?
Bem, meu caro leitor, você deve se lembrar de que ao declararmos uma variável, estamos de fato executando algo. Porém, quando estamos lidando com classes, que diferente de outros tipos de dados e estruturas de registro. Temos sim, a execução de algum tipo de tarefa um tanto quanto mais complexa. No caso de uma classe, como volto a repetir, no momento em que o compilador encontrar a declaração da mesma, ele irá procurar o constructor da classe. Caso não exista algum, ele irá providenciar um que sirva para inicializar a classe de maneira adequada. Caso seu código contenha um constructor adequado sendo definido, o compilador irá criar uma chamada a fim de executar exatamente aquele código definido dentro do constructor que você estiver gerando.
Assim, quando a linha nove for executada, ela de fato estará fazendo uma chamada ao constructor da classe. E com este constructor se encontra no arquivo de código 07, na linha oito. Todo aquele código será executado, criando assim o objeto que estamos definindo deste o início deste artigo. E este objeto irá ficar presente no gráfico, até que o indicador, que no caso é o código 10, seja definitivamente removido do gráfico. Quando isto ocorrer, a variável global, que é vista na linha nove do código 10, será destruída. E neste exato momento, o código do destructor será chamado. Ou seja, voltaremos a executar tudo que estiver implementado dentro da chamada vista na linha 18 do código 07.
Como eu disse no início, as coisas nem sempre são o que parecem ser, quando o assunto é programação orientada em objetos. Por isto que você precisa estudar e praticar antes de sair por aí, imaginando já ter um conhecimento, sobre algo que você ainda não sabe de fato.
Certo, meu caro amigo autor. Concordo com você a este respeito. Mas gostaria de lhe pergunta uma coisa: Se este código 10, faz as coisas da maneira como você acabou de explicar. Gostaria de saber uma coisa, antes de terminarmos: Existe alguma forma de que eu possa controlar, o momento em que o código da classe será executado? Isto com o objetivo de não ter um código todo maluco e doido. Onde o simples fato de declaramos uma variável a fim de utilizar uma classe, já faz o constructor ser executado. E aí, existe uma maneira de controlarmos isto? Sim meu caro leitor. Mas para não tornar este artigo ainda mais confuso. Não iremos ver isto neste exato momento.
Considerações finais
Este talvez, venha a ser o artigo que mais fará pessoas ficarem sem saber o que fazer. Isto porque, apesar de tentar explicar as coisas da maneira o mais simples e didática possível. É praticamente impossível que você venha a compreender todo este conteúdo, apenas lendo o artigo. É de fato necessário que você, procure estudar e praticar o que foi visto aqui. Isto a fim de realmente conseguir compreender como as coisas funcionam e por que elas funcionam daquela maneira.
Sei que este assunto é bastante confuso inicialmente. Por conta disto, passei uma boa parte do tempo estudando e tentando entender C++. Apesar de alguns acharem que dá para aprender C++ em poucos dias, sinto informar que isto não é algo que de fato acontece. C++ demanda meses e até mesmo anos para ser completamente dominado. Isto a ponto de você ser capaz de parar e dizer: Sim, eu sou um programador C++. No entanto, MQL5, não é C++. Na verdade, MQL5, é infinitamente muito mais simples que C++. Porém isto não quer dizer que todo aquele conhecimento adquirido em anos de programação, não estão me servindo. Pelo contrário. Muito do que estou explicando e demonstrando neste artigo, vem de toda a minha experiência e dificuldade em aprender programação orientada em objetos.
Sendo assim, meu caro leitor, se agarre de maneira firme no conhecimento que estou repassando. E comece a praticar e estudar o que está sendo mostrado nesta pequena sequência de artigos. Pois a partir do próximo, a coisa começa a ficar um tanto quanto mais confusa. Isto se você não tiver de fato compreendido o que foi visto e explicado aqui. Então nos vemos no próximo artigo, onde irei explicar como controlar melhor esta questão referente ao uso das classes. Isto a fim de evitar o que é visto no código 10.
| Arquivo MQ5 | Descrição |
|---|---|
| Indicador\Code 01 | Arquivo de demonstração |
| Indicador\Code 02 | Arquivo de demonstração |
| Script\Code 01 | Arquivo de demonstração |
| Script\Code 02 | Arquivo de demonstração |
| Script\Code 03 | Arquivo de demonstração |
| Script\Code 04 | Arquivo de demonstração |
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.
Ondas triangulares e em forma de serra: ferramentas para o trader
Introdução ao MQL5 (Parte 10): Um Guia para Iniciantes sobre como Trabalhar com Indicadores Embutidos no MQL5
Algoritmo de Otimização de Força Central (Central Force Optimization, CFO)
Redes neurais em trading: Detecção adaptativa de anomalias de mercado (Conclusão)
- 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