Domande su OOP in MQL5 - pagina 20

 
Koldun Zloy:
variabile statica, sarà la stessa per tutte le istanze senza ereditarietà.

Ho controllato, ebbene sì, si è rivelato più semplice di così!

/+------------------------------------------------------------------+
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: inizializzato

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

Vedo che la variabile Language è stata inizializzata solo una volta, cioè questa è una soluzione più semplice

Grazie!

 
Roman:

Per favore, ditemi, in cosa è diverso?
Creare un oggetto, o puntatore, in questo modo

dalla creazione classica

La differenza è che nel primo caso, non è necessario specificare l'identificatore di classeCClass in C++.

 

Ho abbozzato la mia classe, che dovrebbe inizializzare i campi una volta con valori costanti, e sembra funzionare come previsto:

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;
  }
//+------------------------------------------------------------------+

Non mi piacciono 2 cose:

1. Ripeto chiamando SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - perché l'ordine di inizializzazione non è definito, cioè non è sicuro che VolumeSTEP sarà inizializzato prima e solo alloraGetDigitsInVolumeStep() sarà chiamato

2. Voglio sbarazzarmi del metodo statico static int GetDigitsInVolumeStep() - ho visto un video su youtube che dice che in OOP puro non si dovrebbero usare metodi statici, e ora sto combattendo contro i mulini a vento

il link al video, è essenzialmente lo stessohttps://youtu.be/lfdAwl3-X_c ehttps://youtu.be/zME4SOCHT0I


come posso riscrivere questi 2 punti che non mi piacciono in modo diverso?

 
Igor Makanu:

1. Ripeto la chiamata a SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - perché l'ordine di inizializzazione non è specificato, cioè non è certo che avrò VolumeSTEP inizializzato per primo, e solo allora GetDigitsInVolumeStep() sarà chiamato

1. cioè avete paura che l'ordine di inizializzazione della statica sia indefinito? In realtà non lo è, i plus si inizializzano in ordine di definizione nel codice. Quindi non preoccupatevi, prima VolumeSTEP e poi VolumeDIGITS.

 
Vict:

1. Quindi avete paura che l'ordine di inizializzazione della statica sia indefinito? In realtà non è così, in plus l'inizializzazione è in ordine di definizione nel codice. Quindi non preoccupatevi, prima VolumeSTEP e poi VolumeDIGITS.

Sì, non che io abbia paura, diciamo che sono protetto, credo che il codice dovrebbe avere almeno qualche garanzia contro i cambiamenti nel comportamento di compilazione - in generale SI

ZS: qui in generale, perché l'argomento per la prima volta un giorno - voglio vedere come realistico per fare qualcosa come nel video autore Egor dice, mentre dubito che si ottiene qualcosa di efficace - ora sto usando metodi statici, e con l'inizializzazione sono già alcuni dubbi apparso

 
Vict:

1. cioè avete paura che l'ordine di inizializzazione della statica sia indefinito? In realtà non lo è, i plus si inizializzano in ordine di definizione nel codice. Quindi non preoccupatevi, prima VolumeSTEP e poi VolumeDIGITS.

Anche in MQL.

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

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

Sì, non che io abbia paura, diciamo che sono protetto, credo che il codice dovrebbe avere almeno qualche garanzia contro i cambiamenti nel comportamento di compilazione - in generale SI

ZS: qui in generale, perché l'argomento per la prima volta un giorno - voglio vedere come realistico per fare qualcosa come nel video autore Egor dice, mentre dubito che otterremo qualcosa di efficace - ora sto usando metodi statici, e con l'inizializzazione sono già alcuni dubbi apparso

#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 

Per usarlo in questo modo:

#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 in un codice come questo:

CTradeConst* tradeConst=new CTradeConst;
CExampe* exsample[10];
for (int i=0;i<10;example[i++]=new CExample(_Symbol,tradeConst));
 
L'opzione del campo statico ha un enorme svantaggio. Non si possono avere valori diversi di questo campo in diverse istanze della classe.
 
Vladimir Simakov:

Utilizzare come segue:

E nel codice come questo:

oops, un po' di sviamento, ecco cosa posso fare

Ecco cosa voglio ottenere in generale:

1. la classe CDeal è autonoma, apre/chiude/controlla il suo ordine con una strategia - le strategie sono enumerate, così l'ottimizzatore può scorrere il mix di strategie

2. secondo il passo 1, non ho bisogno da nessuna parte di avere le variabili di ambiente terminale SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_MIN,SYMBOL_VOLUME_STEP e la quantità di caratteri in SYMBOL_VOLUME_STEP - non cambia durante il funzionamento del programma MQL

3. partendo dai punti 1 e 2, posso incapsulare i metodi di apertura/chiusura/ negoziazione degli ordini e le proprietà degli ordini stessi in una classe CDeal - non userò tutto questo da qualche parte? - cioè posso usarli tutti in una classe


pp 1-3 tutto è risolvibile, ma... OK, ora mi avete aiutato con la statica, che sia così, perché ho un riferimento, almeno posso giustificare la mia decisione in qualche modo, ora il codice è così

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();
//+------------------------------------------------------------------+

creato 3 istanze di Cdeal, l'ho trovato nel registro:

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

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

Finora, tutto ha funzionato come previsto!


Cioè, ora non ci sono sezioni di codice ripetuto (chiamate) - è un codice efficiente, ma come liberarsi dall'uso della funzione statica static int Cdeal::GetDigitsInVolumeStep() - nel video, l'autore crede che sia possibile scrivere codice OOP senza funzioni statiche, io vedo che non solo è scomodo, ma non è sempre possibile usare tutto, a proposito dei metodi Get e Set - molto probabilmente ne farò a meno, vedrò più tardi


Vladimir Simakov:
L'opzione del campo statico ha un enorme svantaggio. Non si possono avere valori diversi di questo campo in diverse istanze della classe.

Sì, grazie, lo so, lo scopo è solo quello di ottenere un codice efficiente senza variabili eccessive e uso di memoria inutile



Non ho ancora risolto la questione:come evitare di usare la funzione statica static int Cdeal::GetDigitsInVolumeStep()

 
Igor Makanu:

Ecco uno schema della mia classe, che dovrebbe inizializzare i campi una volta con valori costanti, sembra funzionare come previsto:

È solo una classe di prova o la userete davvero?
Se è la seconda, non dovresti farlo in quel modo. Le variabili statiche non vengono reinizializzate quando cambia il simbolo, per quanto mi ricordi.
E in generale, non è una buona idea inizializzare la statica costante con valori esterni, che non sono necessariamente disponibili al momento dell'inizializzazione.