Preguntas sobre POO en MQL5 - página 20

 
Koldun Zloy:
Variable estática, será la misma para todas las instancias sin herencia.

Comprobado, pues sí, resultó ser más sencillo que eso.

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

Veo que la variable Language sólo se ha inicializado una vez, es decir, esta es una solución más sencilla

Gracias.

 
Roman:

Por favor, dígame, ¿en qué se diferencia?
Crear un objeto, o puntero, de esta manera

de la creación clásica

La diferencia es que en el primer caso, no es necesario especificar el identificador de claseCClass en C++.

 

He dibujado mi clase, que debería inicializar los campos una vez con valores constantes, y parece que funciona como se pretende:

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

No me gustan dos cosas:

1. Repito la llamada a SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - porque el orden de inicialización no está definido, es decir, no es seguro que primero se inicialice VolumeSTEP y sólo después se llame aGetDigitsInVolumeStep()

2. Quiero deshacerme del método estático static int GetDigitsInVolumeStep() - He visto un video en youtube diciendo que en la POO pura no se deben usar métodos estáticos, y ahora estoy luchando contra molinos de viento

el enlace al video, es esencialmente el mismohttps://youtu.be/lfdAwl3-X_c yhttps://youtu.be/zME4SOCHT0I


¿Cómo puedo reescribir estos 2 puntos que no me gustan de manera diferente?

 
Igor Makanu:

1. Repito la llamada a SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - porque no se especifica el orden de inicialización, es decir, no es seguro que primero se inicialice VolumeSTEP y sólo después se llame a GetDigitsInVolumeStep()

1. ¿Es decir, teme que el orden de inicialización de la estática sea indefinido? En realidad no lo es, los pluses se inicializan por orden de definición en el código. Así que no te preocupes, primero VolumeSTEP y luego VolumeDIGITS.

 
Vict:

1. ¿Así que temes que el orden de inicialización de la estática sea indefinido? En realidad no es así, en los pluses la inicialización es por orden de definición en el código. Así que no te preocupes, primero VolumeSTEP y luego VolumeDIGITS.

Sí, no es que tenga miedo, digamos que estoy protegido, creo que el código debe tener al menos alguna garantía contra los cambios en el comportamiento de la compilación - en general SÍ

ZS: aquí, en general, ¿por qué el tema por primera vez un día - Quiero ver cómo realista para hacer algo como en el autor de vídeo Egor dice, mientras que dudo de que obtenemos algo efectivo - ahora estoy usando métodos estáticos, y con la inicialización son ya algunas dudas apareció

 
Vict:

1. ¿Es decir, teme que el orden de inicialización de la estática sea indefinido? En realidad no lo es, los pluses se inicializan por orden de definición en el código. Así que no te preocupes, primero VolumeSTEP y luego VolumeDIGITS.

También en MQL.

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

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

Sí, no es que tenga miedo, digamos que estoy protegido, creo que el código debe tener al menos alguna garantía contra los cambios en el comportamiento de la compilación - en general SÍ

ZS: aquí, en general, ¿por qué el tema por primera vez un día - Quiero ver cómo realista para hacer algo como en el video autor Egor dice, mientras que dudo que vamos a conseguir algo efectivo - ahora estoy usando métodos estáticos, y con la inicialización son ya algunas dudas aparecieron

#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 usarlo de esta manera:

#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;}
};

Y en un código como este:

CTradeConst* tradeConst=new CTradeConst;
CExampe* exsample[10];
for (int i=0;i<10;example[i++]=new CExample(_Symbol,tradeConst));
 
La opción del campo estático tiene una gran desventaja. No se pueden tener diferentes valores de este campo en diferentes instancias de la clase.
 
Vladimir Simakov:

Utilícelo de la siguiente manera:

Y en el código así:

oops, un poco de desvío, eso es lo que puedo hacer

Esto es lo que quiero conseguir en general:

1. la clase CDeal es autónoma, abre/cierra/controlan su orden por una estrategia - las estrategias se enumeran, por lo que el optimizador se desplaza a través de la mezcla de estrategias

2. de acuerdo con el paso 1, no necesito en ningún otro lugar tener las variables de entorno del terminal SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_MIN,SYMBOL_VOLUME_STEP y la cantidad de caracteres en SYMBOL_VOLUME_STEP - no cambia durante el funcionamiento del programa MQL

3. a partir de los puntos 1 y 2, puedo encapsular los métodos de apertura/cierre/operación de una orden y las propiedades de la orden en sí mismas en una clase CDeal - no voy a usar todo eso en ningún sitio después, ¿verdad? - es decir, puedo utilizarlos todos en una clase


pp 1-3 todo es solucionable, pero... OK, ahora me has ayudado con la estática, que sea así, porque tengo una referencia, al menos puedo fundamentar mi decisión de alguna manera, ahora el código es así

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

creé 3 instancias de Cdeal, lo tengo en el registro:

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

Hasta ahora, todo ha funcionado según lo previsto.


Es decir, ahora no hay secciones de código repetido (llamadas) - es un código eficiente, pero cómo alejarse del uso de la función estática static int Cdeal::GetDigitsInVolumeStep() - en el video, el autor cree que es posible escribir código OOP sin funciones estáticas, veo que no sólo es inconveniente, pero no siempre es posible utilizar todo, sobre los métodos Get y Set - lo más probable es que prescindir de ellos, voy a ver más adelante


Vladimir Simakov:
La opción del campo estático tiene una gran desventaja. No se pueden tener diferentes valores de este campo en diferentes instancias de la clase.

Sí, gracias, ya lo sé, el propósito es sólo conseguir un código eficiente sin exceso de variables y uso de memoria innecesario



Todavía no he resuelto la cuestión:cómo evitar el uso de la función estática static int Cdeal::GetDigitsInVolumeStep()

 
Igor Makanu:

Aquí está un esquema de mi clase, que debe inicializar los campos una vez con valores constantes, parece que funciona como se pretende:

¿Es una clase puramente de prueba o realmente la vas a utilizar?
Si es la segunda, no deberías hacerlo así. Las variables estáticas no se reinicializan cuando cambia el símbolo, que yo recuerde.
Y en general, no es una buena idea inicializar las constantes estáticas con valores externos, que no están necesariamente disponibles en el momento de la inicialización.
Razón de la queja: