Qual é a melhor coisa sobre os modelos?
Os modelos das funções são usados quando é necessário realizar as mesmas operações com diferentes tipos de dados, por exemplo, localizar o elemento máximo na matriz. A principal vantagem da utilização de modelos é que o programador não necessita escrever uma sobrecarga separada para cada tipo. Ou seja, em vez de várias declarações do conjunto de sobrecargas para cada tipo
double ArrayMax(double array[])
{
...
}
int ArrayMax(int array[])
{
...
}
uint ArrayMax(uint array[])
{
...
}
long ArrayMax(long array[])
{
...
}
datetime ArrayMax(datetime array[])
{
...
}
|
basta escrever uma função de modelo
template<typename T>
T ArrayMax(T array[])
{
if(ArraySize()==0)
return(0);
uint max_index=ArrayMaximum(array);
return(array[max_index]);
}
|
e, em seguida, usá-la em seu código:
double high[];
datetime time[];
....
double max_high=ArrayMax(high);
datetime lasttime=ArrayMax(time);
|
Neste caso, o parâmetro formal T especifica o tipo de dados utilizados, durante a compilação, ele é substituído pelo tipo real utilizado, ou seja, o compilador gera automaticamente uma função separada para cada tipo, isto é, double, datetime e assim por diante. Da mesma forma, na linguagem MQL5, você pode criar modelos de classes usando todas as vantagens de tal abordagem.
Modelos de classes
O modelo de classe é declarado usando a palavra-chave template, seguida pelos colchetes angulares <>, nos quais são listados os parâmetros formais com a palavra-chave typename. Este registro indica ao compilador que está perante uma classe genérica na com o parâmetro formal T que especifica o tipo real da variável ao implementar a classe. Por exemplo, criamos uma classe vector para armazenar a matriz com elementos do tipo T:
#define TOSTR(x) #x+" " // macro para exibir o nome do objeto
//+------------------------------------------------------------------+
//| Classe vector para armazenar elementos do tipo T |
//+------------------------------------------------------------------+
template <typename T>
class TArray
{
protected:
T m_array[];
public:
//--- por padrão, o construtor cria uma matriz de 10 elementos
void TArray(void){ArrayResize(m_array,10);}
//--- construtor para criar um vetor com o tamanho definido da matriz
void TArray(int size){ArrayResize(m_array,size);}
//--- retorna o tipo e número de dados que são armazenados no objeto do tipo TArray
string Type(void){return(typename(m_array[0])+":"+(string)ArraySize(m_array));};
};
|
Em seguida, no programa, criamos de maneiras diferentes três objetos TArray para trabalhar com diferentes tipos
void OnStart()
{
TArray<double> double_array; // por padrão, o tamanho do vetor é 10
TArray<int> int_array(15); // o tamanho do vetor é 15
TArray<string> *string_array; // ponteiro para o vetor TArray<string>
//--- criamos o objeto dinâmico
string_array=new TArray<string>(20);
//--- no Diário, exibimos o nome do objeto, tipo de dados e tamanho do vetor
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());
//--- excluímos o objeto dinâmico antes de encerrar o programa
delete(string_array);
}
|
Resultado do script:
double_array (double:10)
int_array (int:15)
string_array (string:20)
|
Como resultado, foram criados 3 vetores com diferentes tipos de dados: double, int e string.
Os modelos de classes são adequados para desenvolver recipientes, isto é, os objetos destinados a encapsular qualquer tipo de objeto. Os objetos dos recipientes são coleções que já contêm objetos de um tipo particular. Normalmente, o recipiente imediatamente é integrado e implementado para trabalhar com dados que são armazenados nele.
Por exemplo, é possível criar um modelo de classe que não permita acessar um elemento fora da matriz e, assim, evitar o erro crítico "out of range".
//+------------------------------------------------------------------+
// | Classe para acessar com segurança um elemento da matriz |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
{
protected:
T m_array[];
public:
//--- construtor por padrão
void TSafeArray(void){}
//--- construtor para criar a matriz do tamanho especificado
void TSafeArray(int size){ArrayResize(m_array,size);}
//--- tamanho de matriz
int Size(void){return(ArraySize(m_array));}
//--- alteração do tamanho da matriz
int Resize(int size,int reserve){return(ArrayResize(m_array,size,reserve));}
//--- libertação da matriz
void Erase(void){ZeroMemory(m_array);}
//--- operador de acesso ao elemento da matriz de acordo com o índice
T operator[](int index);
//--- operador de atribuição para obter imediatamente todos os elementos a partir da matriz
void operator=(const T &array[]); // matriz do tipo T
};
//+------------------------------------------------------------------+
//| Operação de obtenção do elemento segundo o índice |
//+------------------------------------------------------------------+
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 index %d is not in range (0-%d)!",__FUNCTION__,index,max);
return(invalid_value);
}
//---
return(m_array[index]);
}
//+------------------------------------------------------------------+
//| Operação de atribuição para a matriz |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T &array[])
{
int size=ArraySize(array);
ArrayResize(m_array,size);
//--- o tipo T deve suportar o operador de cópia
for(int i=0;i<size;i++)
m_array[i]=array[i];
//---
}
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
int copied,size=15;
MqlRates rates[];
//--- copiamos a matriz de cotações
if((copied=CopyRates(_Symbol,_Period,0,size,rates))!=size)
{
PrintFormat("CopyRates(%s,%s,0,%d) retornou o código de erro %d",
_Symbol,EnumToString(_Period),size,GetLastError());
return;
}
//--- criamos o recipiente e colocamos nele a matriz dos valores MqlRates
TSafeArray<MqlRates> safe_rates;
safe_rates=rates;
//--- índice nos limites da matriz
int index=3;
PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
//--- índice fora dos limites da matriz
index=size;
PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
}
|
Note-se que, na descrição dos métodos fora da declaração da classe, também é necessário utilizar a declaração de modelo:
template<typename T>
T TSafeArray::operator[](int index)
{
...
}
template<typename T>
void TSafeArray::operator=(const T &array[])
{
...
}
|
Os modelos de classes e funções permitem especificar vários parâmetros formais, separados por vírgulas, por exemplo, coleção Map para armazenar os pares "chave - valor":
template<typename Key, template Value>
class TMap
{
...
}
|
Veja também
Modelos de funções, Sobrecarga