模板优势

函数模板用于当您需要在不同数据类型上执行类似操作的时候,例如,寻找数组中的最大元素。 使用模板的主要优势在于您无需为每个类型编制独立的重载代码。而取代声明每个类型的多个重载

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还可以使您使用该方法的全部优势开发类模板。

类模板

类模板使用尖括号<>之前的template关键字进行声明,列举包含typename关键字的形式参数列表。该条目会通知编译器在执行类时它处理的是包含定义真实变量类型的T形式参数的普通类。例如,创建一个矢量类来存储T类型元素数组:

#define TOSTR(x) #x+" "   // 显示对象名称的宏
//+------------------------------------------------------------------+
//| 存储T- 类型元素的矢量类                       |
//+------------------------------------------------------------------+
template <typename T>
class TArray
  {
protected:
   T                 m_array[];
public:
   //--- 构造函数默认创建一个10元素数组
   void TArray(void){ArrayResize(m_array,10);}
   //--- 创建一个指定数组大小矢量类的构造函数
   void TArray(int size){ArrayResize(m_array,size);}
   //--- 返回存储在TArray类型对象中的数据类型和数据量
   string Type(void){return(typename(m_array[0])+":"+(string)ArraySize(m_array));};
  };

接下来,在程序中使用不同的方法创建3个TArray 对象来处理多种类型

void OnStart()
  {
   TArray<double> double_array;   // 矢量默认大小10 
   TArray<int> int_array(15);     // 矢量大小15
   TArray<string> *string_array;  // TArray<string>矢量指针
//--- 创建动态对象
   string_array=new TArray<string>(20);
//--- 在日志中显示对象名称,数据类型和矢量大小
   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());
//--- 完成程序之前删除动态对象
   delete(string_array);   
  }

脚本执行结果:

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

现在,我们有不同数据类型的3种矢量:double,int和string。

类模板非常适合开发容器 – 专为封装任何类型其他对象而设计的对象。容器对象是已经包含特定类型对象的集合。通常,处理存储数据会立刻内置于容器。

例如,您可以创建不允许访问数组外元素的类模板,因此也就是避免了“超范围”的严重错误

//+------------------------------------------------------------------+
//| 自由访问数组元素的类               |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
  {
protected:
   T                 m_array[];
public:
   //--- 默认构造函数
   void              TSafeArray(void){}
   //--- 创建指定大小数组的构造函数
   void              TSafeArray(int size){ArrayResize(m_array,size);}
   //--- 数组大小
   int               Size(void){return(ArraySize(m_array));}
   //--- 改变数组大小
   int               Resize(int size,int reserve){return(ArrayResize(m_array,size,reserve));}
   //--- 释放数组
   void              Erase(void){ZeroMemory(m_array);}
   //--- 通过索引访问数组元素的操作符
   T                 operator[](int index);
   //--- 从数组一次性接收全部元素的赋值操作符
   void              operator=(const T  &array[]); // T 类型数组
  };
//+------------------------------------------------------------------+
//| 通过索引接收元素                           |
//+------------------------------------------------------------------+
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]);
  }
//+------------------------------------------------------------------+
//| 分配数组                                 |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
//--- T 类型应该支持复制操作符
   for(int i=0;i<size;i++)
      m_array[i]=array[i];
//---
  }
//+------------------------------------------------------------------+
//| 脚本程序起始函数                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int copied,size=15;  
   MqlRates rates[];
//--- 复制报价数组
   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;
     }
//--- 创建容器并插入MqlRates值数组
   TSafeArray<MqlRates> safe_rates;
   safe_rates=rates;
   //--- 数组内索引
   int index=3;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
   //--- 数组外索引
   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[])
  {
   ...
  }

类和函数模板允许您定义多个逗号分隔的形式参数,例如,存储"key – value"组的地图集合:

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

 

另见

函数模板重载