Vantaggi dei templates

I Templates delle funzioni vengono utilizzati quando è necessario eseguire operazioni simili su vari tipi di dati, ad esempio, la ricerca di un elemento massimo nell'array. Il vantaggio principale di applicare i templates è che non c'è bisogno di codare un separato overload per ogni tipo. Invece di dichiarare multiple overloads di ogni tipo

double ArrayMax(double array[])
  {
   ...
  }
int ArrayMax(int array[])
  {
   ...
  }
uint ArrayMax(uint array[])
  {
   ...
  }
long ArrayMax(long array[])
  {
   ...
  }
datetime ArrayMax(datetime array[])
  {
   ...
  }

abbiamo bisogno di scrivere una sola funzione template

template<typename T> 
T ArrayMax(T array[])
  {
   if(ArraySize()==0) 
      return(0);
   uint max_index=ArrayMaximum(array);  
   return(array[max_index]);
  }

per utilizzarlo nel codice:

double high[];
datetime time[];
....
double max_high=ArrayMax(high);
datetime lasttime=ArrayMax(time);

Qui, il parametro formale T specificante un tipo di dati utilizzato è sostituito con un tipo effettivamente applicata durante la compilazione, cioè il compilatore genera automaticamente una funzione separata per ogni tipo - double, datetime, eccetera. MQL5 permette inoltre di sviluppare modelli di classi utilizzando tutti i vantaggi di questo approccio.

Templates delle Classi

Un template(modello) della classe è dichiarato con la parola chiave template seguita da parentesi angolari<> enumeranti la lista dei parametri formali con la parola chiave typename. Questa voce informa il compilatore che si tratta di una classe generica con il parametro formale T che definisce un vero e proprio tipo di variabile in sede di attuazione di una classe. Ad esempio, creiamo una classe vettore per la memorizzazione di un array con elementi di tipo T:

#define TOSTR(x) #x+" "   // macro per visualizzare il nome dell'oggetto
//+------------------------------------------------------------------+
//| Classe vettore per memorizzare elementi tipo-T                   |
//+------------------------------------------------------------------+
template <typename T>
class TArray
  {
protected:
   T                 m_array[];
public:
   //--- il costruttore crea un'array per 10 elementi di default
   void TArray(void){ArrayResize(m_array,10);}
   //--- il costruttore per creare un vettore con una grandezza array specificata
   void TArray(int size){ArrayResize(m_array,size);}
   //--- restituisce il tipo ed ammontare di dati conservati nell'oggetto di tipo TArray
   string Type(void){return(typename(m_array[0])+":"+(string)ArraySize(m_array));};
  };

Quindi, cerchiamo di applicare metodi diversi per creare tre oggetti TArray nel programma per lavorare con vari tipi

void OnStart()
  {
   TArray<double> double_array;   // il vettore ha la grandezza di default di 10 
   TArray<int> int_array(15);     // il vettore ha la grandezza di 15
   TArray<string> *string_array;  // puntatore al vettore TArray<string> 
//--- crea un oggetto dinamico
   string_array=new TArray<string>(20);
//--- mostra il nome dell'oggetto, il tipo di dati e grandezza vettore nel Journal
   PrintFormat("%s (%s)",TOSTR(double_array),double_array.Type());
   PrintFormat("%s (%s)",TOSTR(int_array),int_array.Type());
   PrintFormat("%s (%s)",TOSTR(string_array),string_array.Type());
//--- rimuove un oggetto dinamico prima di completare il programma
   delete(string_array);   
  }

Risultati esecuzione script:

  double_array  (double:10)
  int_array  (int:15)
  string_array  (string:20)

Ora, abbiamo 3 vettori con diversi tipi di dati: double, int e string.

I modelli(template) della classe sono adatti per lo sviluppo di contenitori - oggetti progettati per incapsulare altri oggetti di qualsiasi tipo. Gli oggetti container sono raccolte già contenenti oggetti di un certo tipo. Di solito, il lavoro con i dati memorizzati è immediatamente costruito nel contenitore.

Ad esempio, è possibile creare un modello della classe che non consente l'accesso a un elemento al di fuori dell'array, evitando così l' errore critico "out of range" .

//+------------------------------------------------------------------+
//| Classe per il libero accetto agli elementi dell'array            |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
  {
protected:
   T                 m_array[];
public:
   //--- costruttore default 
   void              TSafeArray(void){}
   //--- costruttore per creare l'array di grandezza specificata
   void              TSafeArray(int size){ArrayResize(m_array,size);}
   //--- grandezza array 
   int               Size(void){return(ArraySize(m_array));}
   //--- cambia la grandezza array
   int               Resize(int size,int reserve){return(ArrayResize(m_array,size,reserve));}
   //--- rilascia l'array 
   void              Erase(void){ZeroMemory(m_array);}
   //--- operatore per accedere agli elementi dell'array per indice
   T                 operator[](int index);
   //--- assegnazione dell'operatore per ricevere tutti gli elementi dall'array tutto in una volta
   void              operator=(const T  &array[]); // T type array 
  };
//+------------------------------------------------------------------+
//| Ricezione di un elemento per indice                              |
//+------------------------------------------------------------------+
template<typename T>
T TSafeArray::operator[](int index)
  {
   static T invalid_value;
//---
   int max=ArraySize(m_array)-1;
   if(index<0 || index>=ArraySize(m_array))
     {
      PrintFormat("%s indice %d non è nel range (0-%d)!",__FUNCTION__,index,max);
      return(invalid_value);
     }
//---
   return(m_array[index]);
  }
//+------------------------------------------------------------------+
//| Assegnazione per l'rray                                          |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
//--- Tipo T deve supportare la copia dell'operatore
   for(int i=0;i<size;i++)
      m_array[i]=array[i];
//---
  }
//+------------------------------------------------------------------+
//| Funzione di start del programma Script                           |
//+------------------------------------------------------------------+
void OnStart()
  {
   int copied,size=15;  
   MqlRates rates[];
//--- copia l'array delle quotazioni
   if((copied=CopyRates(_Symbol,_Period,0,size,rates))!=size)
     {
      PrintFormat("CopyRates(%s,%s,0,%d) ha restituito il codice errore %d",
      _Symbol,EnumToString(_Period),size,GetLastError());
      return;
     }
//--- crea un contenintore ed inserisce i valori MqlRates in esso
   TSafeArray<MqlRates> safe_rates;
   safe_rates=rates;
   //--- indice dentro l'array
   int index=3;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
   //--- indice fuori l'array
   index=size;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
  }

Si prega di notare che la dichiarazione del template dev essere utilizzata anche quando si descrivono i metodi fuori della dichiarazione della classe:

template<typename T>
T TSafeArray::operator[](int index)
  {
   ...
  }
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   ...
  }

Modelli della classe e funzionalità consentono di definire più parametri formali separati da virgole, ad esempio, la raccolta Map per la memorizzazione di coppie "valori-chiave":

template<typename Key, template Value>
class TMap
  {
   ...
  }

 

Guarda anche

Templates funzioni, Overload