操作重载

为了便于代码读写,允许重载一些操作。使用关键字operator编写重载操作符。下面这些操作符可以重载:

  • 二进制 +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
  • 一进制 +,-,++,--,!,~
  • 赋值操作符 =
  • 索引操作符 []

 

操作重载允许使用操作记号(以简单的表达式书写)用于复杂的对象 - 结构和类。使用重载操作书写表达式简化了源代码视图,因为隐藏了更复杂的实施。

例如,考虑复数,其中包括实数和虚数。它们广泛用于算术学。MQL5语言没有表示复数的数据类型,但是可以用结构或类的形式创建新的数据类型。声明复杂结构并定义四种实施四个算术运算的方法:

//+------------------------------------------------------------------+
//| 复数操作结构                                                       |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // 实数
   double            im; // 虚数
   //--- 构造函数
                     complex():re(0.0),im(0.0) {  }
                     complex(const double r):re(r),im(0.0) {  }
                     complex(const double r,const double i):re(r),im(i) {  }
                     complex(const complex &o):re(o.re),im(o.im) { }
   //--- 算术运算
   complex           Add(const complex &l,const complex &r) const;  // 加法
   complex           Sub(const complex &l,const complex &r) const;  // 减法
   complex           Mul(const complex &l,const complex &r) const;  // 乘法
   complex           Div(const complex &l,const complex &r) const;  // 除法
  };

现在,在我们的代码中我们可以声明表示复数的变量,并使用它们。

例如:

void OnStart()
  {
//--- 声明并初始化复杂类型的变量
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- 两个数字总和
   complex z;
   z=a.Add(a,b);
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- 两个数字相乘
   z=a.Mul(a,b);
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- 两个数字相除
   z=a.Div(a,b);
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }

但是对于复数的普通算术操作,使用一般的操作符"+", "-", "*" 和 "/"会更加方便。

关键字操作符用于定义执行类型转换的成员函数。类对象变量的一进制和二进制操作可以作为非静态成员函数重载。它们在类对象上隐式操作。

大多数二进制操作可以像普通函数一样重载,采取一个或两个参数作为类变量或该类对象的指针。为了类型复杂,声明重载类似如下:

   //--- 操作符
   complex operator+(const complex &r) const { return(Add(this,r)); }
   complex operator-(const complex &r) const { return(Sub(this,r)); }
   complex operator*(const complex &r) const { return(Mul(this,r)); }
   complex operator/(const complex &r) const { return(Div(this,r)); }

所有脚本示例:

//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 声明和初始化复杂类型的变量
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
   //a.re=5;
   //a.im=1;
   //b.re=-1;
   //b.im=-5;
//--- 两个数字总和
   complex z=a+b;
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- 两个数字相乘
 
   z=a*b;
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- 两个数字相除
   z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }
//+------------------------------------------------------------------+
//| 复数操作结构                                                       |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // 实数
   double            im; // 虚数
   //--- 构造函数
                     complex():re(0.0),im(0.0) {  }
                     complex(const double r):re(r),im(0.0) {  }
                     complex(const double r,const double i):re(r),im(i) {  }
                     complex(const complex &o):re(o.re),im(o.im) { }
   //--- 算术运算
   complex           Add(const complex &l,const complex &r) const;  // 加法
   complex           Sub(const complex &l,const complex &r) const;  // 减法
   complex           Mul(const complex &l,const complex &r) const;  // 乘法
   complex           Div(const complex &l,const complex &r) const;  // 除法
   //--- 二进制操作符
   complex operator+(const complex &r) const { return(Add(this,r)); }
   complex operator-(const complex &r) const { return(Sub(this,r)); }
   complex operator*(const complex &r) const { return(Mul(this,r)); }
   complex operator/(const complex &r) const { return(Div(this,r)); }
  };
//+------------------------------------------------------------------+
//| 加法                                                              |
//+------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re+r.re;
   res.im=l.im+r.im;
//--- 结果
   return res;
  }
//+------------------------------------------------------------------+
//| 减法                                                              |
//+------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re-r.re;
   res.im=l.im-r.im;
//--- 结果
   return res;
  }
//+------------------------------------------------------------------+
//| 乘法                                                              |
//+------------------------------------------------------------------+
complex complex::Mul(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re*r.re-l.im*r.im;
   res.im=l.re*r.im+l.im*r.re;
//--- 结果
   return res;
  }
//+------------------------------------------------------------------+
//| 除法                                                              |
//+------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
  {
//--- 空复数
   complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- 检查零
   if(r.re==0 && r.im==0)
     {
      Print(__FUNCTION__+": number is zero");
      return(res);
     }
//--- 辅助变量
   double e;
   double f;
//--- 选择计算变量
   if(MathAbs(r.im)<MathAbs(r.re))
     {
      e = r.im/r.re;
      f = r.re+r.im*e;
      res.re=(l.re+l.im*e)/f;
      res.im=(l.im-l.re*e)/f;
     }
   else
     {
      e = r.re/r.im;
      f = r.im+r.re*e;
      res.re=(l.im+l.re*e)/f;
      res.im=(-l.re+l.im*e)/f;
     }
//--- 结果
   return res;
  }

 

大部分一进制的类操作可以作为接受单类对象参数或其指针的普通函数进行重载。添加重载一进制操作 "-" 和 "!"。

//+------------------------------------------------------------------+
//| 复数操作结构                                                       |
//+------------------------------------------------------------------+
struct complex
  {
   double            re;       // 实数
   double            im;       // 虚数
...
   //--- 一进制操作符
   complex operator-()  const// 一进制减操作
   bool    operator!()  const// 非操作
  };
...
//+------------------------------------------------------------------+
//| 重载"一进制减" 操作符                                               |
//+------------------------------------------------------------------+
complex complex::operator-() const
  {
   complex res;
//---
   res.re=-re;
   res.im=-im;
//--- 结果
   return res;
  }
//+------------------------------------------------------------------+
//| 重载 "逻辑非" 操作符                                               |
//+------------------------------------------------------------------+
bool complex::operator!() const
  {
//--- 复数的实数和虚数等于零吗?
   return (re!=0 && im!=0);
  }

 

现在我们可以检查零复数值并获得一个负值:

//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 声明和初始化复杂类型的变量
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- 两个数字相除
   complex z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- 复数默认等于零(默认构造函数re==0 和 im==0)
   complex zero;
   Print("!zero=",!zero);
//--- 分配负值
   zero=-z;
   PrintFormat("z=%.2f+i*%.2f,  zero=%.2f+i*%.2f",z.re,z.im, zero.re,zero.im);
   PrintFormat("-zero=%.2f+i*%.2f",-zero.re,-zero.im);
//--- 再次检查零值 
   Print("!zero=",!zero);
//---
  }

注意我们无需重载赋值操作符 "=",因为 简单类型的结构 可以直接复制成另一个。因此,我们现在可以用普通方式书写计算涉及复数的代码。

重载索引操作符允许以简单熟悉的方式,获得对象内附有的数组值,它也有助于更好地阅读源代码。例如,我们需要在指定位置提供访问符号字符串。MQL5字符串是一个单独的类型字符串,它并不是符号数组,但是在重载索引操作的帮助下,我们可以在生成的CString类中提供简单透明的工作:

//+------------------------------------------------------------------+
//| 访问类似符号数组的字符串符号的类                                      |
//+------------------------------------------------------------------+
class CString
  {
   string            m_string;
  
public:
                     CString(string str=NULL):m_string(str) { }
   ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
  };
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnStart()  
  {
//--- 从字符串接收符号的数组
   int     x[]={ 19,4,18,19,27,14,15,4,17,0,19,14,17,27,26,28,27,5,14,
                 17,27,2,11,0,18,18,27,29,30,19,17,8,13,6 };
   CString str("abcdefghijklmnopqrstuvwxyz[ ]CS");
   string  res;
//--- 使用来自str变量的符号组成短语
   for(int i=0,n=ArraySize(x);i<n;i++)
     {
      res+=ShortToString(str[x[i]]);
     }
//--- 显示结果
   Print(res);
  }

另一个重载索引操作的示例就是矩阵操作。矩阵表示一个二维动态数组,数组大小不会提前定义。因此,您不能声明没有指定第二维度大小的数组形式array[][],以及作为参数通过该数组。可能的解决方案就是一个特别的类CMatrix,它包含CRow 类对象数组。

//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 矩阵的加法和乘法操作
   CMatrix A(3),B(3),C();
//--- 准备数组行
   double a1[3]={1,2,3}, a2[3]={2,3,1}, a3[3]={3,1,2};
   double b1[3]={3,2,1}, b2[3]={1,3,2}, b3[3]={2,1,3};
//--- 填补矩阵
   A[0]=a1; A[1]=a2; A[2]=a3;
   B[0]=b1; B[1]=b2; B[2]=b3;
//--- 在专家日志输出矩阵
   Print("---- Elements of matrix A");
   Print(A.String());
   Print("---- Elements of matrix B");
   Print(B.String());
 
//--- 矩阵加法
   Print("---- Addition of matrices A and B");
   C=A+B;
//--- 输出格式化字符串表示
   Print(C.String());
 
//--- 矩阵乘法
   Print("---- Multiplication of matrices A and B");
   C=A*B;
   Print(C.String());
 
//--- 现在我们展示如何在动态数组matrix[i][j]风格下得到值
   Print("Output the values of matrix C elementwise");
//--- 通过矩阵行 - CRow 对象 - 循环
   for(int i=0;i<3;i++)
     {
      string com="| ";
      //--- 形成矩阵的行值
      for(int j=0;j<3;j++)
        {
         //--- 通过行数和列数获得矩阵元素
         double element=C[i][j];// [i] - 在数组 m_rows[]访问CRow ,
                                // [j] - 重载CRow索引操作符
         com=com+StringFormat("a(%d,%d)=%G ; ",i,j,element);
        }
      com+="|";
      //--- 输出行值
      Print(com);
     }
  }
//+------------------------------------------------------------------+
//| 类 "行"                                                           |
//+------------------------------------------------------------------+
class CRow
  {
private:
   double            m_array[];
public:
   //--- 构造函数和析构函数
                     CRow(void)          { ArrayResize(m_array,0);    }
                     CRow(const CRow &r) { this=r;                    }
                     CRow(const double &array[]);
                    ~CRow(void){};
   //--- 行的元素数
   int               Size(voidconst    { return(ArraySize(m_array));}
   //--- 返回一个字符串值 
   string            String(voidconst;
   //--- 索引操作符
   double            operator[](int i) const  { return(m_array[i]);   }
   //--- 赋值操作符 
   void              operator=(const double  &array[]); // 数组
   void              operator=(const CRow & r);         // 另一个 CRow 对象
   double            operator*(const CRow &o);          // CRow 对象乘法
  };
//+------------------------------------------------------------------+
//| 构造函数初始化数组行                                                |
//+------------------------------------------------------------------+
void  CRow::CRow(const double &array[])
  {
   int size=ArraySize(array);
//--- 如果数组不为空
   if(size>0)
     {
      ArrayResize(m_array,size);
      //--- 填值
      for(int i=0;i<size;i++)
         m_array[i]=array[i];
     }
//---
  }
//+------------------------------------------------------------------+
//| 数组赋值操作                                                       |
//+------------------------------------------------------------------+
void CRow::operator=(const double &array[])
  {
   int size=ArraySize(array);
   if(size==0) return;
//--- 填充数组值
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=array[i];
//--- 
  }
//+------------------------------------------------------------------+
//|  CRow 赋值操作                                                    |
//+------------------------------------------------------------------+
void CRow::operator=(const CRow  &r)
  {
   int size=r.Size();
   if(size==0) return;
//--- 填充数组值
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=r[i];
//--- 
  }
//+------------------------------------------------------------------+
//| 另一行乘法的操作符                                                  |
//+------------------------------------------------------------------+
double CRow::operator*(const CRow &o)
  {
   double res=0;
//--- 验证
   int size=Size();
   if(size!=o.Size() || size==0)
     {
      Print(__FUNCSIG__,": Failed to multiply two matrices, their sizes are different");
      return(res);
     }
//--- 乘以数组elementwise 并添加产品
   for(int i=0;i<size;i++)
      res+=m_array[i]*o[i];
//--- 结果
   return(res);
  }
//+------------------------------------------------------------------+
//| 返回格式化字符串表示方式                                            |
//+------------------------------------------------------------------+
string CRow::String(voidconst
  {
   string out="";
//--- 如果数组大小大于零
   int size=ArraySize(m_array);
//--- 我们只使用一个非零的数组元素
   if(size>0)
     {
      out="{";
      for(int i=0;i<size;i++)
        {
         //--- 收集字符串的值
         out+=StringFormat(" %G;",m_array[i]);
        }
      out+=" }";
     }
//--- 结果
   return(out);
  }
//+------------------------------------------------------------------+
//| 类 "Matrix"                                                      |
//+------------------------------------------------------------------+
class CMatrix
  {
private:
   CRow              m_rows[];
 
public:
   //--- 构造函数和析构函数
                     CMatrix(void);
                     CMatrix(int rows)  { ArrayResize(m_rows,rows);             }
                    ~CMatrix(void){};
   //--- 得到矩阵大小
   int               Rows()       const { return(ArraySize(m_rows));            }
   int               Cols()       const { return(Rows()>0? m_rows[0].Size():0); }
   //--- 以CRow行的形式返回列的值
   CRow              GetColumnAsRow(const int col_index) const;
   //--- 返回字符串矩阵值 
   string            String(voidconst;
   //--- 索引操作符返回一个字符串数字
   CRow *operator[](int i) const        { return(GetPointer(m_rows[i]));        }
   //--- 加法操作符
   CMatrix           operator+(const CMatrix &m);
   //--- 乘法操作符
   CMatrix           operator*(const CMatrix &m);
   //--- 赋值操作符
   CMatrix          *operator=(const CMatrix &m);
  };
//+------------------------------------------------------------------+
//| 默认构造函数,创建和零行的数组                                        |
//+------------------------------------------------------------------+
CMatrix::CMatrix(void)
  {
//--- 零矩阵的行数
   ArrayResize(m_rows,0);
//---  
  }
//+------------------------------------------------------------------+
//| 返回CRow形式的列值                                                 |
//+------------------------------------------------------------------+
CRow  CMatrix::GetColumnAsRow(const int col_index) const
  {
//--- 获得列值的变量
   CRow row();
//--- 矩阵的行数
   int rows=Rows();
//--- 如果大于零的行数,执行操作
   if(rows>0)
     {
      //--- 接收col_index索引列的值的数组
      double array[];
      ArrayResize(array,rows);
      //--- 填充数组
      for(int i=0;i<rows;i++)
        {
         //--- 检查i行的列数 - 它可能超过数组的限制
         if(col_index>=this[i].Size())
           {
            Print(__FUNCSIG__,": Error! Column number ",col_index,"> row size ",i);
            break// 行将是未初始化对象
           }
         array[i]=this[i][col_index];
        }
      //--- 基于数组值创建CRow行
      row=array;
     }
//--- 结果
   return(row);
  }
//+------------------------------------------------------------------+
//| 两个矩阵的加法                                                     |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator+(const CMatrix &m)
  {
//--- 通过矩阵的行数和列数
   int cols=m.Cols();
   int rows=m.Rows();
//--- 接收相加结果的矩阵
   CMatrix res(rows);
//--- 矩阵大小必须匹配
   if(cols!=Cols() || rows!=Rows())
     {
      //--- 无法相加
      Print(__FUNCSIG__,": Failed to add two matrices, their sizes are different");
      return(res);
     }
//--- 辅助数组
   double arr[];
   ArrayResize(arr,cols);
//--- 通过行相加
   for(int i=0;i<rows;i++)
     {
      //--- 写下数组中矩阵字符串的加法结果
      for(int k=0;k<cols;k++)
        {
         arr[k]=this[i][k]+m[i][k];
        }
      //--- 将数组放在矩阵行
      res[i]=arr;
     }
//--- 返回矩阵相加结果
   return(res);
  }
//+------------------------------------------------------------------+
//| 两个矩阵的乘法                                                     |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator*(const CMatrix &m)
  {
//--- 第一个矩阵的列数,通过矩阵的行数
   int cols1=Cols();
   int rows2=m.Rows();
   int rows1=Rows();
   int cols2=m.Cols();
//--- 接收相加结果的矩阵
   CMatrix res(rows1);
//--- 矩阵应该协调
   if(cols1!=rows2)
     {
      //--- 不能相乘
      Print(__FUNCSIG__,": Failed to multiply two matrices, format is not compatible "
            "- number of columns in the first factor should be equal to the number of rows in the second");
      return(res);
     }
//--- 辅助数组
   double arr[];
   ArrayResize(arr,cols1);
//--- 在矩阵乘法中填写该行
   for(int i=0;i<rows1;i++)// 通过行
     {
      //--- 重置接收数组
      ArrayInitialize(arr,0);
      //--- 通过行的元素
      for(int k=0;k<cols1;k++)
        {
         //--- 利用m矩阵的k列的值用于CRow
         CRow column=m.GetColumnAsRow(k);
         //--- 两行相乘,在i-th元素写下矢量标量乘法的结果
         arr[k]=this[i]*column;
        }
      //--- 将数组arr[]至于矩阵i-th行
      res[i]=arr;
     }
//--- 返回两个矩阵的产物
   return(res);
  }
//+------------------------------------------------------------------+
//| 赋值操作                                                          |
//+------------------------------------------------------------------+
CMatrix *CMatrix::operator=(const CMatrix &m)
  {
//--- 查找和设置行数
   int rows=m.Rows();
   ArrayResize(m_rows,rows);
//--- 填充我们传递矩阵行值的行
   for(int i=0;i<rows;i++) this[i]=m[i];
//---
   return(GetPointer(this));
  }
//+------------------------------------------------------------------+
//| 矩阵的字符串表示                                                   |
//+------------------------------------------------------------------+
string CMatrix::String(voidconst
  {
   string out="";
   int rows=Rows();
//--- 形成字符串的字符串
   for(int i=0;i<rows;i++)
     {
      out=out+this[i].String()+"\r\n";
     }
//--- 结果
   return(out);
  }

另见

重载算术运算函数重载优先规则