跳至内容

Class Templates

模板的优势

函数模板适用于需要在不同类型的数据上执行类似操作的情况,例如,在数组中查找最大元素。使用模板的主要优势是无需为每种类型单独编写重载函数。无需为每种类型声明多个重载版本

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

我们只需要编写一个模板函数

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

以便在代码中使用:

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

在这里,指定数据类型的T形式参数在编译时被替换为实际使用的类型,即编译器会自动为每个类型生成单独的函数——doubledatetime等。MQL5还允许您利用该方法的全部优势来开发类模板。

类模板

类模板使用模板关键字声明,后面是尖括号<>,列出形式参数的列表,并使用typename关键字。此声明告知编译器正在处理一个通用类,其中T形式参数在实现类时定义了实际变量类型。例如,让我们创建一个用于存储T类型元素的向量的类:

#define TOSTR(x) #x+" "   // macro for displaying an object name
//+------------------------------------------------------------------+
//| Vector class for storing T-type elements                       |
//+------------------------------------------------------------------+
template <typename T>
class TArray
  {
protected:
   T                 m_array[];
public:
   //--- constructor creates an array for 10 elements by default
   void TArray(void){ArrayResize(m_array,10);}
   //--- constructor for creating a vector with a specified array size
   void TArray(int size){ArrayResize(m_array,size);}
   //--- return a type and amount of data stored in the TArray type object
   string Type(void){return(typename(m_array[0])+":"+(string)ArraySize(m_array));};
  };

接下来,让我们应用不同的方法在程序中创建三个TArray对象,以便处理各种类型

void OnStart()
  {
   TArray<double> double_array;   // vector has a default size of 10
   TArray<int> int_array(15);     // vector has a size of 15
   TArray<string> *string_array;  // pointer to TArray<string> vector
//--- create a dynamic object
   string_array=new TArray<string>(20);
//--- display an object name, data type and vector size in the 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());
//--- remove a dynamic object before completing the program
   delete(string_array);
  }

脚本执行结果:

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

现在,我们有3个具有不同数据类型的向量:double、int和string。

类模板非常适合开发容器——用于封装任何其他类型对象的对象。容器对象是已经包含特定类型对象的集合。通常,存储的数据操作会立即内置到容器中。

例如,您可以创建一个类模板,禁止访问数组外的元素,从而避免“超出范围”的严重错误

//+------------------------------------------------------------------+
//| Class for a free access to an array element               |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
  {
protected:
   T                 m_array[];
public:
   //--- default constructor
   void              TSafeArray(void){}
   //--- constructor for creating the array of a specified size
   void              TSafeArray(int size){ArrayResize(m_array,size);}
   //--- array size
   int               Size(void){return(ArraySize(m_array));}
   //--- change the array size
   int               Resize(int size,int reserve){return(ArrayResize(m_array,size,reserve));}
   //--- release the array
   void              Erase(void){ZeroMemory(m_array);}
   //--- operator for accessing the array element by index
   T                 operator[](int index);
   //--- assignment operator for receiving all elements from the array at once
   void              operator=(const T  &array[]); // T type array
  };
//+------------------------------------------------------------------+
//| Receiving an element by index                           |
//+------------------------------------------------------------------+
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]);
  }
//+------------------------------------------------------------------+
//| Assigning for the array                                 |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
//--- T type should support the copying operator
   for(int i=0;i<size;i++)
      m_array[i]=array[i];
//---
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int copied,size=15;
   MqlRates rates[];
//--- copy the array of quotes
   if((copied=CopyRates(_Symbol,_Period,0,size,rates))!=size)
     {
      PrintFormat("CopyRates(%s,%s,0,%d) returned %d error code",
      _Symbol,EnumToString(_Period),size,GetLastError());
      return;
     }
//--- create a container and insert the MqlRates value array to it
   TSafeArray<MqlRates> safe_rates;
   safe_rates=rates;
   //--- index within the array
   int index=3;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
   //--- index outside the array
   index=size;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
  }

请注意,在描述类声明之外的方法时也应使用模板声明:

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

类模板和函数模板允许定义多个逗号分隔的形式参数,例如,用于存储“键-值”对的Map集合:

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

另请参阅

函数模板重载

最后更新于