Perguntas sobre OOP em MQL5 - página 20

 
Koldun Zloy:
Variável estática, será a mesma para todas as instâncias sem herança.

Checado, bem sim! acabou sendo mais simples do que isso!

/+------------------------------------------------------------------+
string MyTerminalInfoString()
  {
   Print(__FUNCTION__);
   return("ru");
  }
//+------------------------------------------------------------------+
class B
  {
  protected:
   static const string Language;
public:
                     B() {Print(__FUNCTION__,Language); }
  };
//+------------------------------------------------------------------+
static const string B::Language=MyTerminalInfoString();
//+------------------------------------------------------------------+
B *arrB[5];
//+------------------------------------------------------------------+
int OnInit()
  {
   for(int i=0; i<5; i++)
      arrB[i] = new B;
   return(INIT_SUCCEEDED);
  }

2019.08.29 15:14:09.847 tst__ EURUSD,M15: inicializado

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: MyTerminalInfoString

Vejo que a variável Idioma só foi inicializada uma vez, ou seja, esta é uma solução mais simples

Obrigado!

 
Roman:

Por favor, diga-me, qual é a diferença?
Criar um objeto, ou ponteiro, desta forma

da criação clássica

A diferença é que, no primeiro caso, não é necessário especificar o identificador de classeCClass em C++.

 

Eu esbocei minha classe, que deveria inicializar os campos uma vez com valores constantes, e parece funcionar como pretendido:

class Cdeal : public CObject
  {

private:
   static const double VolumeMAX;
   static const double VolumeMIN;
   static const double VolumeSTEP;
   static const int  VolumeDIGITS;
   static int        GetDigitsInVolumeStep();
   SSettingsForOrder m_set;
public:
                     Cdeal(SSettingsForOrder &set);
  };
//____________________________________________________________________
static int Cdeal::GetDigitsInVolumeStep()
  {
   long i=10000000,k=long(::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP)/0.0000001);
   int result=0;
   while(result<7 && k%i>0)
     {
      i/=10;
      result++;
     }
   return(result);
  }
//____________________________________________________________________
static const double Cdeal::VolumeMAX = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
//____________________________________________________________________
static const double Cdeal::VolumeMIN = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
//____________________________________________________________________
static const double Cdeal::VolumeSTEP = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
//____________________________________________________________________
static const int Cdeal::VolumeDIGITS = Cdeal::GetDigitsInVolumeStep();
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
Cdeal::Cdeal(SSettingsForOrder &set)
  {
   m_set = set;
  }
//+------------------------------------------------------------------+

Eu não gosto de 2 coisas:

1. repito a chamada SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - porque a ordem de inicialização não está definida, ou seja, não é certo que o VolumeSTEP será inicializado primeiro e só entãoGetDigitsInVolumeStep() será chamado

2. quero me livrar do método estático no GetDigitsInVolumeStep() - Vi um vídeo no youtube dizendo que no OOP puro não se deve usar métodos estáticos, e agora estou lutando contra moinhos de vento

o link para o vídeo, é essencialmente o mesmohttps://youtu.be/lfdAwl3-X_c ehttps://youtu.be/zME4SOCHT0I


como posso reescrever estes 2 pontos que não me agradam de maneira diferente?

 
Igor Makanu:

1. repito a chamada para SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - porque a ordem de inicialização não está especificada, ou seja, não é certo que eu terei o VolumeSTEP inicializado primeiro, e só então GetDigitsInVolumeStep() será chamado

1. Ou seja, você tem medo de que a ordem de inicialização estática seja indefinida? Na verdade não é, as vantagens se inicializam em ordem de definição no código. Portanto, não se preocupe, primeiro VolumeSTEP e depois VolumeDIGITS.

 
Vict:

1) Então você teme que a ordem de inicialização estática seja indefinida? Na verdade, não é assim, em plus inicialização está em ordem de definição em código. Portanto, não se preocupe, primeiro VolumeSTEP e depois VolumeDIGITS.

Sim, não que eu tenha medo, digamos que estou protegido, acredito que o código deve ter pelo menos alguma garantia contra mudanças no comportamento de compilação - em geral SIM

ZS: Aqui em geral, por que o tópico para o uma vez apaed por dia - eu quero ver como realista para fazer algo como no vídeo que Egor diz, enquanto eu duvido que consigamos algo efetivo - agora eu estou usando métodos estáticos, e com a inicialização já surgiram algumas dúvidas

 
Vict:

1. Ou seja, você tem medo de que a ordem de inicialização da estática seja indefinida? Na verdade não é, as vantagens se inicializam em ordem de definição no código. Portanto, não se preocupe, primeiro VolumeSTEP e depois VolumeDIGITS.

Também na MQL.

https://www.mql5.com/ru/docs/basis/oop/staticmembers

Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
  • www.mql5.com
Члены класса могут быть объявлены с использованием модификатора класса памяти static. Такие члены данных разделяются всеми экземплярами данного класса и хранятся в одном месте. Нестатические члены данных создаются для каждой переменной-объекта класса. Отсутствие возможности объявлять статически члены класса привело бы к необходимости объявлять...
 
Igor Makanu:

Sim, não que eu tenha medo, digamos que estou protegido, acredito que o código deve ter pelo menos alguma garantia contra mudanças no comportamento de compilação - em geral SIM

ZS: aqui em geral, porque o tema pela primeira vez por dia - quero ver como é realista fazer algo como no vídeo que Egor diz, enquanto duvido que consigamos algo efetivo - agora estou usando métodos estáticos, e com a inicialização já surgiram algumas dúvidas

#ifndef _C_TRADE_CONST_
#define _C_TRADE_CONST_

#define TRADE_CONST_DECL  CTradeConst* cTradeConst
#define TRADE_CONST  cTradeConst
#define _symbol      cTradeConst.symbol
#define _lotDigits   cTradeConst.lotDigits
#define _lotStep     cTradeConst.lotStep
#define _lotMax      cTradeConst.lotMax
#define _lotMin      cTradeConst.lotMin
#define _tickSize    cTradeConst.tickSize
#define _point       cTradeConst.point
#define _stopLevel   cTradeConst.stopLevel
#define _freezeLevel cTradeConst.freezeLevel
#define _digits      cTradeConst.digits
#ifdef __MQL5__
   #define _marginMode        cTradeConst.marginMode
   #define _executionMode     cTradeConst.executionMode
   #define _expirationMode    cTradeConst.expirationMode
   #define _fillingMode       cTradeConst.fillingMode
   #define _orderMode         cTradeConst.orderMode
#endif 

class CTradeConst
  {
public:
   string                  symbol;
   int                     lotDigits;
   double                  lotStep;
   double                  lotMax;
   double                  lotMin;
   double                  tickSize;
   double                  point;
   double                  stopLevel;
   double                  freezeLevel;
   int                     digits;
#ifdef __MQL5__
   ENUM_ACCOUNT_MARGIN_MODE      marginMode;
   ENUM_SYMBOL_TRADE_EXECUTION   executionMode;
   int                           expirationMode;
   int                           fillingMode;
   int                           orderMode;
#endif   
public:
                     CTradeConst(string mSymbol):symbol(mSymbol==NULL?_Symbol:mSymbol){if (!Init()) delete GetPointer(this);}
private:
   bool              Init();
  };
bool CTradeConst::Init(void){
   if (!MQLInfoInteger(MQL_TESTER)&&!SymbolSelect(symbol,true)) return false;
   lotStep=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   lotDigits=MathMax(-(int)MathFloor(MathLog10(lotStep)),0);
   lotMax=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   lotMin=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   point=SymbolInfoDouble(symbol,SYMBOL_POINT);
   tickSize=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE);
   stopLevel=SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL)*point;
   freezeLevel=SymbolInfoInteger(symbol,SYMBOL_TRADE_FREEZE_LEVEL)*point;
   digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
#ifdef __MQL5__
   marginMode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   executionMode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(symbol,SYMBOL_TRADE_EXEMODE);
   fillingMode=(int)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
   expirationMode=(int)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_MODE);
   orderMode=(int)SymbolInfoInteger(symbol,SYMBOL_ORDER_MODE);
#endif 
   return true;}
   
#endif 

Para utilizá-lo desta forma:

#include "CTradeConst.mqh"

#define  FLAG_CONST_CREATE_HERE   0x1

class CExample{
   TRADE_CONST_DECL;
   int cFlag;
                  CExample(string mSymbol,CTradeConst* mTradeConst):
                     TRADE_CONST(!CheckPointer(mTradeConst)?new CTradeCons(mSymbol)t:mTradeConst)
                     {if (!CheckPointer(mTradeConst)) cFlag|=FLAG_CONST_CREATE_HERE;}
                 ~CExample()  {if (bool(cFlag&FLAG_CONST_CREATE_HERE)) delete TRADE_CONST;}
};

E em código como este:

CTradeConst* tradeConst=new CTradeConst;
CExampe* exsample[10];
for (int i=0;i<10;example[i++]=new CExample(_Symbol,tradeConst));
 
A opção de campo estático tem uma enorme desvantagem. Você não pode ter valores diferentes deste campo em diferentes instâncias da classe.
 
Vladimir Simakov:

Use da seguinte forma:

E no código como este:

oops, um pouco mal dirigido, é o que eu posso fazer

Eis o que eu quero alcançar em geral:

1. a classe CDeal é autônoma, ela abre/fecha/controla sua ordem por uma estratégia - as estratégias são enumeradas, então o otimizador percorre a mistura de estratégias

2. De acordo com o passo 1, eu não preciso de nenhum outro lugar para ter as variáveis de ambiente terminal SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_MIN,SYMBOL_VOLUME_STEP e quantidade de caracteres em SYMBOL_VOLUME_STEP - não muda durante a operação do programa MQL

3. A partir dos itens 1 e 2, posso encapsular os métodos de abertura/fecho/trading de uma ordem e as próprias propriedades da ordem em uma classe CDeal - não vou usar tudo isso em nenhum lugar depois, vou? - ou seja, posso usar todos eles em uma classe


pp 1-3 tudo é solvível, mas... OK, agora você me ajudou com a estática, que assim seja, porque eu tenho uma referência, pelo menos eu posso fundamentar minha decisão de alguma forma, agora o código é assim

class Cdeal : public CObject
  {
private:
   static const double VolumeMAX;
   static const double VolumeMIN;
   static const double VolumeSTEP;
   static const int  VolumeDIGITS;
   static int        GetDigitsInVolumeStep();
   SSettingsForOrder m_set;
public:
                     Cdeal() { Print(__FUNCTION__,"VolumeMAX = ",VolumeMAX," , VolumeMIN = ",VolumeMIN," , VolumeSTEP = ",VolumeSTEP," , VolumeDIGITS = ",VolumeDIGITS); }
  };
//____________________________________________________________________
static int Cdeal::GetDigitsInVolumeStep()
  {
   long i=10000000,k=long(VolumeSTEP/0.0000001);
   int result=0;
   while(result<7 && k%i>0)
     {
      i/=10;
      result++;
     }
   Print(__FUNCTION__);
   return(result);
  }
//____________________________________________________________________
static const double Cdeal::VolumeMAX = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
//____________________________________________________________________
static const double Cdeal::VolumeMIN = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
//____________________________________________________________________
static const double Cdeal::VolumeSTEP = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
//____________________________________________________________________
static const int Cdeal::VolumeDIGITS = Cdeal::GetDigitsInVolumeStep();
//+------------------------------------------------------------------+

criou 3 instâncias do Cdeal, conseguiu-o no log:

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Inicializado

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::GetDigitsInVolumeStep

Até agora, tudo tem funcionado como planejado!


Isto é, agora não há seções de código repetido (chamadas) - é um código eficiente, mas como fugir do uso de funções estáticas int Cdeal::GetDigitsInVolumeStep() - no vídeo, o autor acredita que é possível escrever código OOP sem funções estáticas, vejo que não só é inconveniente, mas nem sempre é possível usar tudo, sobre os métodos Get and Set - muito provavelmente farei sem eles, verei mais tarde


Vladimir Simakov:
A opção de campo estático tem uma enorme desvantagem. Você não pode ter valores diferentes deste campo em diferentes instâncias da classe.

Sim, obrigado, eu sei que o objetivo é apenas obter um código eficiente sem variáveis excessivas e uso desnecessário de memória.



Ainda não resolvi a questão:como evitar o uso da função estática int Cdeal::GetDigitsInVolumeStep()

 
Igor Makanu:

Aqui está um esboço da minha aula, que deve inicializar os campos uma vez com valores constantes, parece funcionar como pretendido:

É puramente uma aula de teste ou você realmente vai usá-la?
Se é o segundo, não se deve fazer assim. As variáveis estáticas não são reinicializadas quando o símbolo muda, pelo que me lembro.
E, em geral, não é uma boa idéia inicializar estáticas constantes com valores externos, que não estão necessariamente disponíveis no momento da inicialização.
Razão: