跳至内容

Operation Overloading

操作重载

为了便于阅读和理解代码,允许对某些操作进行重载。使用关键字“operator”来定义重载运算符。以下运算符可以被重载:

  • 二元运算符 +, -, /, *, %, «, », ==,!=, <, >, <=, >=, =, +=, -=, /=, *=, %=, &=, |=, ^=, «=, »=, &&, ||, &, |, ^
  • 一元运算符 +, -, ++, –, !, ~
  • 赋值运算符 =
  • 索引运算符 []

操作重载允许使用简单的表达式形式来表示复杂的对象——结构和类。使用重载的运算符编写表达式可以简化源代码的视图,因为更复杂的实现被隐藏了起来。

例如,考虑由实部和虚部组成的复数。它们在数学中广泛使用。MQL4语言没有表示复数的数据类型,但可以通过结构或类创建新的数据类型。声明复数结构并定义四个实现四种算术运算的方法:

//+------------------------------------------------------------------+
//| A structure for operations with complex numbers                  |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // Real part
   double            im; // Imaginary part
   //--- Constructors
                     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) { }
   //--- Arithmetic operations
   complex           Add(const complex &l,const complex &r) const;  // Addition
   complex           Sub(const complex &l,const complex &r) const;  // Subtraction
   complex           Mul(const complex &l,const complex &r) const;  // Multiplication
   complex           Div(const complex &l,const complex &r) const;  // Division
  };

现在,在我们的代码中可以声明表示复数的变量,并对其进行操作。

例如:

void OnStart()
  {
//--- Declare and initialize variables of a complex type
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Sum up two numbers
   complex z;
   z=a.Add(a,b);
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiply two numbers
   z=a.Mul(a,b);
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Divide two numbers
   z=a.Div(a,b);
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }

但是,使用常见的运算符“+”、“-”、“*”和“/”进行复数的普通算术运算会更方便。

关键字“operator”用于定义执行类型转换的成员函数。类对象变量的一元和二元运算符可以作为非静态成员函数重载。它们隐式地作用于类对象。

大多数二元运算符可以像常规函数一样被重载,这些函数接受一个或两个参数作为类变量或指向该类的对象的指针。对于我们的复数类型,在声明中的重载如下所示:

//--- Operators
   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)); }

脚本的完整示例:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Declare and initialize variables of type complex
   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;
//--- Sum up two numbers
   complex z=a+b;
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiply two numbers

   z=a*b;
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Divide two numbers
   z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }
//+------------------------------------------------------------------+
//| A structure for operations with complex numbers                  |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // Real part
   double            im; // Imaginary part
   //--- Constructors
                     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) { }
   //--- Arithmetic operations
   complex           Add(const complex &l,const complex &r) const;  // Addition
   complex           Sub(const complex &l,const complex &r) const;  // Subtraction
   complex           Mul(const complex &l,const complex &r) const;  // Multiplication
   complex           Div(const complex &l,const complex &r) const;  // Division
   //--- Binary operators
   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)); }
  };
//+------------------------------------------------------------------+
//| Addition                                                         |
//+------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re+r.re;
   res.im=l.im+r.im;
//--- Result
   return res;
  }
//+------------------------------------------------------------------+
//| Subtraction                                                      |
//+------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re-r.re;
   res.im=l.im-r.im;
//--- Result
   return res;
  }
//+------------------------------------------------------------------+
//| Multiplication                                                   |
//+------------------------------------------------------------------+
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;
//--- Result
   return res;
  }
//+------------------------------------------------------------------+
//| Division                                                         |
//+------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
  {
//--- Empty complex number
   complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- Check for zero
   if(r.re==0 && r.im==0)
     {
      Print(__FUNCTION__+": number is zero");
      return(res);
     }
//--- Auxiliary variables
   double e;
   double f;
//--- Selecting calculation variant
   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;
     }
//--- Result
   return res;
  }

大多数一元运算符可以作为接受单个类对象参数或指向它的指针的普通函数来重载。添加对一元运算符“-”和“!”的重载。

//+------------------------------------------------------------------+
//| Structure for operations with complex numbers                    |
//+------------------------------------------------------------------+
struct complex
  {
   double            re;       // Real part
   double            im;       // Imaginary part
...
   //--- Unary operators
   complex operator-()  const; // Unary minus
   bool    operator!()  const; // Negation
  };
...
//+------------------------------------------------------------------+
//| Overloading the "unary minus" operator                           |
//+------------------------------------------------------------------+
complex complex::operator-() const
  {
   complex res;
//---
   res.re=-re;
   res.im=-im;
//--- Result
   return res;
  }
//+------------------------------------------------------------------+
//| Overloading the "logical negation" operator                      |
//+------------------------------------------------------------------+
bool complex::operator!() const
  {
//--- Are the real and imaginary parts of the complex number equal to zero?
   return (re!=0 && im!=0);
  }

现在我们可以检查复数的值是否为零并得到负值:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Declare and initialize variables of type complex
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Divide the two numbers
   complex z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- A complex number is equal to zero by default (in the default constructor re==0 and im==0)
   complex zero;
   Print("!zero=",!zero);
//--- Assign a negative value
   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);
//--- Check for zero once again
   Print("!zero=",!zero);
//---
  }

注意,我们不必重载赋值运算符“=”,因为简单类型的结构可以直接相互复制。因此,我们现在可以用常规方式编写涉及复数的计算代码。

索引运算符的重载允许以简单且熟悉的方式获取对象中数组的值,这也提高了源代码的可读性。例如,我们需要提供对字符串中指定位置的符号的访问。MQL4中的字符串是一个独立的类型string,它不是符号的数组,但借助重载的索引操作,我们可以为生成的CString类提供简单且透明的工作方式:

//+------------------------------------------------------------------+
//| Class to access symbols in string as in array of symbols         |
//+------------------------------------------------------------------+
class CString
  {
   string            m_string;

public:
                     CString(string str=NULL):m_string(str) { }
   ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- An array for receiving symbols from a string
   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;
//--- Make up a phrase using symbols from the str variable
   for(int i=0,n=ArraySize(x);i<n;i++)
     {
      res+=ShortToString(str[x[i]]);
     }
//--- Show the result
   Print(res);
  }

索引操作重载的另一个例子是矩阵运算。矩阵表示一个二维动态数组,数组的大小不是预先定义的。因此,你不能在不指定第二维大小的情况下声明形式为array[][]的数组,然后将其作为参数传递。一个可能的解决方案是特殊的类CMatrix,它包含CRow类对象的数组。

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Operations of addition and multiplication of matrices
   CMatrix A(3),B(3),C();
//--- Prepare an array for rows
   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};
//--- Fill the matrices
   A[0]=a1; A[1]=a2; A[2]=a3;
   B[0]=b1; B[1]=b2; B[2]=b3;
//--- Output the matrices in the Experts log
   Print("---- Elements of matrix A");
   Print(A.String());
   Print("---- Elements of matrix B");
   Print(B.String());

//--- Addition of matrices
   Print("---- Addition of matrices A and B");
   C=A+B;
//--- Output the formatted string representation
   Print(C.String());

//--- Multiplication of matrices
   Print("---- Multiplication of matrices A and B");
   C=A*B;
   Print(C.String());

//--- Now we show how to get values in the style of dynamic arrays matrix[i][j]
   Print("Output the values of matrix C elementwise");
//--- Go through the matrix rows - CRow objects - in a loop
   for(int i=0;i<3;i++)
     {
      string com="| ";
      //--- Form rows from the matrix for the value
      for(int j=0;j<3;j++)
        {
         //--- Get the matrix element by the number of the row and column
         double element=C[i][j];// [i] - Access to CRow in the array m_rows[] ,
                                // [j] - Overloaded operator of indexing in CRow
         com=com+StringFormat("a(%d,%d)=%G ; ",i,j,element);
        }
      com+="|";
      //--- Output the values of the row
      Print(com);
     }
  }
//+------------------------------------------------------------------+
//| Class "Row"                                                      |
//+------------------------------------------------------------------+
class CRow
  {
private:
   double            m_array[];
public:
   //--- Constructors and a destructor
                     CRow(void)          { ArrayResize(m_array,0);    }
                     CRow(const CRow &r) { this=r;                    }
                     CRow(const double &array[]);
                    ~CRow(void){};
   //--- Number of elements in the row
   int               Size(void) const    { return(ArraySize(m_array));}
   //--- Returns a string with values
   string            String(void) const;
   //--- Indexing operator
   double            operator[](int i) const  { return(m_array[i]);   }
   //--- Assignment operators
   void              operator=(const double  &array[]); // An array
   void              operator=(const CRow & r);         // Another CRow object
   double            operator*(const CRow &o);          // CRow object for multiplication
  };
//+------------------------------------------------------------------+
//| Constructor for initializing a row with an array                 |
//+------------------------------------------------------------------+
void  CRow::CRow(const double &array[])
  {
   int size=ArraySize(array);
//--- If the array is not empty
   if(size>0)
     {
      ArrayResize(m_array,size);
      //--- Fill with values
      for(int i=0;i<size;i++)
         m_array[i]=array[i];
     }
//---
  }
//+------------------------------------------------------------------+
//| Assignment operation for the array                               |
//+------------------------------------------------------------------+
void CRow::operator=(const double &array[])
  {
   int size=ArraySize(array);
   if(size==0) return;
//--- Fill the array with values
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=array[i];
//---
  }
//+------------------------------------------------------------------+
//| Assignment operation for CRow                                    |
//+------------------------------------------------------------------+
void CRow::operator=(const CRow  &r)
  {
   int size=r.Size();
   if(size==0) return;
//--- Fill the array with values
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=r[i];
//---
  }
//+------------------------------------------------------------------+
//| Operator of multiplication by another row                        |
//+------------------------------------------------------------------+
double CRow::operator*(const CRow &o)
  {
   double res=0;
//--- Verifications
   int size=Size();
   if(size!=o.Size() || size==0)
     {
      Print(__FUNCSIG__,": Failed to multiply two matrices, their sizes are different");
      return(res);
     }
//--- Multiply arrays elementwise and add the products
   for(int i=0;i<size;i++)
      res+=m_array[i]*o[i];
//--- Result
   return(res);
  }
//+------------------------------------------------------------------+
//| Returns a formatted string representation                        |
//+------------------------------------------------------------------+
string CRow::String(void) const
  {
   string out="";
//--- If the size of the array is greater than zero
   int size=ArraySize(m_array);
//--- We work only with a non-zero number of array elements
   if(size>0)
     {
      out="{";
      for(int i=0;i<size;i++)
        {
         //--- Collect the values to a string
         out+=StringFormat(" %G;",m_array[i]);
        }
      out+=" }";
     }
//--- Result
   return(out);
  }
//+------------------------------------------------------------------+
//| Class "Matrix"                                                   |
//+------------------------------------------------------------------+
class CMatrix
  {
private:
   CRow              m_rows[];

public:
   //--- Constructors and a destructor
                     CMatrix(void);
                     CMatrix(int rows)  { ArrayResize(m_rows,rows);             }
                    ~CMatrix(void){};
   //--- Get the matrix sizes
   int               Rows()       const { return(ArraySize(m_rows));            }
   int               Cols()       const { return(Rows()>0? m_rows[0].Size():0); }
   //--- Returns the value of the column in the form of a CRow row
   CRow              GetColumnAsRow(const int col_index) const;
   //--- Returns a string with the matrix values
   string            String(void) const;
   //--- The indexing operator returns a string by its number
   CRow *operator[](int i) const        { return(GetPointer(m_rows[i]));        }
   //--- Addition operator
   CMatrix           operator+(const CMatrix &m);
   //--- Multiplication operator
   CMatrix           operator*(const CMatrix &m);
   //--- Assignment operator
   CMatrix          *operator=(const CMatrix &m);
  };
//+------------------------------------------------------------------+
//| Default constructor, create and array of rows of zero size       |
//+------------------------------------------------------------------+
CMatrix::CMatrix(void)
  {
//--- The zero number of rows in the matrix
   ArrayResize(m_rows,0);
//---
  }
//+------------------------------------------------------------------+
//| Returns the column value in the form of CRow                     |
//+------------------------------------------------------------------+
CRow  CMatrix::GetColumnAsRow(const int col_index) const
  {
//--- A variable to get the values from the column
   CRow row();
//--- The number of rows in the matrix
   int rows=Rows();
//--- If the number of rows is greater than zero, execute the operation
   if(rows>0)
     {
      //--- An array to receive the values of the column with index col_index
      double array[];
      ArrayResize(array,rows);
      //--- Filling the array
      for(int i=0;i<rows;i++)
        {
         //--- Check the number of the column for row i - it may exceed the boundaries of the array
         if(col_index>=this[i].Size())
           {
            Print(__FUNCSIG__,": Error! Column number ",col_index,"> row size ",i);
            break; // row will be uninitialized object
           }
         array[i]=this[i][col_index];
        }
      //--- Create a CRow row based on the array values
      row=array;
     }
//--- Result
   return(row);
  }
//+------------------------------------------------------------------+
//| Addition of two matrices                                         |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator+(const CMatrix &m)
  {
//--- The number of rows and columns in the passed matrix
   int cols=m.Cols();
   int rows=m.Rows();
//--- The matrix to receive the addition results
   CMatrix res(rows);
//--- The sizes of the matrix must match
   if(cols!=Cols() || rows!=Rows())
     {
      //--- Addition impossible
      Print(__FUNCSIG__,": Failed to add two matrices, their sizes are different");
      return(res);
     }
//--- Auxiliary array
   double arr[];
   ArrayResize(arr,cols);
//--- Go through rows to add
   for(int i=0;i<rows;i++)
     {
      //--- Write the results of addition of matrix strings in the array
      for(int k=0;k<cols;k++)
        {
         arr[k]=this[i][k]+m[i][k];
        }
      //--- Place the array to the matrix row
      res[i]=arr;
     }
//--- return the result of addition of matrices
   return(res);
  }
//+------------------------------------------------------------------+
//| Multiplication of two matrices                                   |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator*(const CMatrix &m)
  {
//--- Number of columns of the first matrix, number of rows passed in the matrix
   int cols1=Cols();
   int rows2=m.Rows();
   int rows1=Rows();
   int cols2=m.Cols();
//--- Matrix to receive the addition result
   CMatrix res(rows1);
//--- Matrices should be coordinated
   if(cols1!=rows2)
     {
      //--- Multiplication impossible
      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);
     }
//--- Auxiliary array
   double arr[];
   ArrayResize(arr,cols1);
//--- Fill the rows in the multiplication matrix
   for(int i=0;i<rows1;i++)// Go through rows
     {
      //--- Reset the receiving array
      ArrayInitialize(arr,0);
      //--- Go through elements in the row
      for(int k=0;k<cols1;k++)
        {
         //--- Take values of column k of the matrix m in the for of CRow
         CRow column=m.GetColumnAsRow(k);
         //--- Multiply two rows and write the result of scalar multiplication of vectors in the i-th element
         arr[k]=this[i]*column;
        }
      //--- place array arr[] in the i-th row of the matrix
      res[i]=arr;
     }
//--- Return the product of two matrices
   return(res);
  }
//+------------------------------------------------------------------+
//| Assignment operation                                             |
//+------------------------------------------------------------------+
CMatrix *CMatrix::operator=(const CMatrix &m)
  {
//--- Find and set the number of rows
   int rows=m.Rows();
   ArrayResize(m_rows,rows);
//--- Fill our rows with the values of rows of  the passed matrix
   for(int i=0;i<rows;i++) this[i]=m[i];
//---
   return(GetPointer(this));
  }
//+------------------------------------------------------------------+
//| String representation of the matrix                              |
//+------------------------------------------------------------------+
string CMatrix::String(void) const
  {
   string out="";
   int rows=Rows();
//--- Form string by string
   for(int i=0;i<rows;i++)
     {
      out=out+this[i].String()+"\r\n";
     }
//--- Result
   return(out);
  }

另请参阅

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

最后更新于