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