トップ  >  リファレンス  >  基本  >  関数  >  演算子のオーバーロード
スポンサーリンク
検索
カスタム検索
リファレンスツリー
オススメ
演算子のオーバーロード


コードの読み取りと書き込みを容易にする為、いくつかの演算子のオーバーロードが許可されています。
演算子のオーバーロードは、演算子キーワードを使用して書かれます。
次の演算子についてオーバーロードする事が出来ます。
  • 二項 +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
  • 単項 +,-,++,--,!,~
  • 代入演算子 =
  • インデックス作成演算子 []



演算子のオーバーロードは複雑なオブジェクト(構造体やクラス)の演算表記(単純な式の形で書かれた)を使用する事が出来ます。
オーバーロードされた演算子を使用して式を書く事で、ソースコードの表示がシンプルになります。
なぜならより複雑な実装が隠されているからです。

例えば、実部と虚部で構成された複素数を検討してみましょう。
これは数学で幅広く使用されています。
MQL4言語には複素数を表現するデータ型はありません、しかし構造体またはクラスのフォームで新しいデータ型を作成する事が可能です。
複雑な構造体を宣言し、四則演算を実装する4つのメソッドを定義します。



//+------------------------------------------------------------------+
//| 複素数演算の為の構造体                  
//+------------------------------------------------------------------+
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型の変数を宣言して初期化
   complex a(2,4) , b(-4,-2);
   PrintFormat( "a = %.2f + i * %.2f,   b = %.2f+i*%.2f",
                a.re, a.im, b.re, b.im );

   // 2つの数を足す
   complex z;
   z = a.Add(a,b);
   PrintFormat( "a + b = %.2f + i * %.2f",
                z.re, z.im );

   // 2つの数をかける
   z = a.Mul(a,b);
   PrintFormat( "a * b = %.2f + i * %.2f",
               z.re, z.im);

   // 2つの数を割る
   z = a.Div(a,b);
   PrintFormat( "a / b = %.2f + i * %.2f",
               z.re, z.im);
}



しかし、複素数の算術演算(「/」「+」「-」「*」「/」)を使用する方が便利です。

演算子キーワードは型変換を実行するメンバ関数を定義する為に使用されます。
クラスオブジェクト変数の単項演算と二項演算は非静的メンバ関数としてオーバーロードする事が出来ます。
これらは暗黙的にクラスオブジェクトに作用します。

ほとんどの二項演算は、クラスオブジェクトへのポインタまたはクラス変数の、1つまたは両方の引数を受ける通常の関数のようにオーバーロードする事が出来ます。
例のcomplex型の場合、宣言でオーバーロードすると次のようになります。



   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型の変数を宣言して初期化
   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;

   // 2つの数を足す
   complex z;
   z = a.Add(a,b);
   PrintFormat( "a + b = %.2f + i * %.2f",
                z.re, z.im );

   // 2つの数をかける
   z = a.Mul(a,b);
   PrintFormat( "a * b = %.2f + i * %.2f",
               z.re, z.im);

   // 2つの数を割る
   z = a.Div(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 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__ + ": 値がゼロです。");
      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 {

   bool tempbool;

   // 複素数の実部と虚部が等しいかチェック
   if ( re != 0 && im != 0 ) {
       tempbool = true;
   } else {
       tempbool = false;
   }

   return  tempbool;
}



複素数の値がゼロかチェックし、負の値を取得する。


//+------------------------------------------------------------------+
//| スクリプトプログラムスタートイベント                                    
//+------------------------------------------------------------------+
void OnStart() {

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

   // 2つの数を割る
   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);

}



シンプルなデータ型の構造体がお互いに直接コピーする事が出来る為、代入演算子「=」のオーバーロードする必要がなかった事に注意してください。
このように通常の方法で複素数を含む計算をする為コードを書く事が出来ます。

インデックス付け演算子のオーバーロードは、単純でお馴染みの方法でオブジェクトで囲まれた配列の値を取得する事ができ、 ソースコードの読み易さに貢献します。
例えば、指定した位置の文字列内のシンボルへのアクセスを可能にする必要があるとします。
MQL4の文字列は、シンボルの配列では無い個別のstring型です、しかしインデックス演算のオーバーロードの助けを借りて、 生成された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[][]フォームの配列を宣言する事は出来ませんし、 パラメータとしてこの配列を渡す事も出来ません。
可能な解決策はCRowクラスオブジェクト配列が含まれている特別なCMatrixクラスです。



//+------------------------------------------------------------------+
//| 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];
   }

   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+=" }";
   }


   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;
  }

   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";
   }

   return(out);
}


スポンサーリンク



Copyright ©2015 MT4でEA自作しちゃお〜 All Rights Reserved.


Top

inserted by FC2 system