トップ  >  リファレンス  >  基本  >  オブジェクト指向プログラミング  >  ポリモーフィズム
スポンサーリンク
検索
カスタム検索
リファレンスツリー
オススメ

天才数学者のエドワード・オークリー・ソープの物語とジョン・ケリーの公式は必見です。
ポリモーフィズム


ポリモーフィズムは継承によって関連した異なるオブジェクトクラスで同じ関数要素を呼び出す時に使われます。
これは基本クラスの動作を記述する普遍的なメカニズムを作成するのに役に立ち、子孫クラスにも役に立ちます。

基本クラスCShapeの開発を続けましょう。
GetAera()関数メンバを定義し、形状の面積を計算するように設計します。
基本クラスから継承する事によって生成された全ての下位クラスは、特定の形状の面積を計算する規則に従って関数を再定義します。

正方形(CSquareクラス)では面積がその側面を介して計算され、円(CCircleクラス)では面積が半径等で表現されます。
基本クラスと全ての子孫クラスの両方のオブジェクトを格納する事が出来るCShape型のオブジェクト配列を作成する事が出来ます。
さらに配列の各要素に対して同じ関数を呼び出す事が出来ます。



例:
// 基本クラス
class CShape {

protected: 
   int            m_type;                // 形状タイプ
   int            m_xpos;                // 基点のX座標
   int            m_ypos;                // 基点のY座標

public:
   void           CShape() { m_type=0; };       // コンストラクタ タイプ0
   int            GetType(){ return( m_type );};// 形状タイプを取得

virtual
   double         GetArea(){ return (0); }      // 形状の面積を取得
};



今、派生クラスの全てがゼロの値を返すメンバ関数getArea()を持っています。
この関数の実装は各子孫で異なります。


// 派生クラス(円形)
class CCircle : public CShape {            // コロンの後に基本クラスを定義する

private:
   double         m_radius;                // 円の半径

public:
   void           CCircle(){ m_type=1; };   // コンストラクタ タイプ1
   void           SetRadius(double r){ m_radius = r;};
   virtual double GetArea(){ return (3.14 * m_radius * m_radius);} // 円面積
};



正方形クラスの宣言も同じです


// 派生クラス(正方形)
class CSquare : public CShape {            // コロンの後に基本クラスを定義する

private:
   double         m_square_side;                // 正方形の側面

public:
   void           CSquare(){ m_type=2; };   // コンストラクタ タイプ2
   void           SetSide(double s){ m_square_side = s;};
   virtual double GetArea(){ return ( m_square_side * m_square_side);} // 正方形面積
};



正方形と面の面積を計算する為に、m_radiusとm_square_sideの値が必要となるので、対応するクラス宣言にSetRadius()とSetSide()関数を追加しました。

1つの基本型CShapeから派生した異なるタイプのオブジェクト(CCircleとCSquare)がプログラムで使用されていると仮定します。
ポリモーフィズムは基本クラスCShapeのオブジェクト配列を作成する事が出来ますが、配列の宣言時はこれらのオブジェクトは未だ不明で型も定義されていません。

配列の各要素に含まれるオブジェクトタイプの決定は、プログラム実行中に直接行われます。
これは適切なクラスのオブジェクトの動的作成が関係し、オブジェクトの代わりにオブジェクトポインタを使用します。

new演算子はオブジェクトの動的作成に使用します。
このようなオブジェクトは個別にdelete演算子を使用して明示的に削除する必要があります。

CShape型のポインタ配列を宣言し、次のスクリプトの例に示すように各要素(new Class_Name)の為の適切な型のオブジェクトを作成します。



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

   // 基本型のオブジェクトポインタ配列を宣言
   CShape *shapes[5];   // CShapeオブジェクトポインタ配列
 
   // CCircle型のオブジェクトポインタを宣言し、派生オブジェクト配列に書き込む
   CCircle *circle = new CCircle();

   // circleポインタでオブジェクトプロパティ設定
   circle.SetRadius(2.5);

   // shapes[0]にポインタ値を設定
   shapes[0] = circle;
 
   // 別のCCircleオブジェクトを作成し、shapes[1]にそのポインタを書き込む
   circle    = new CCircle();
   shapes[1] = circle;
   circle.SetRadius(5);
 
   // 意図的にshapes[2]の値を設定しない(設定処理をコメントアウト)
   //circle = new CCircle();
   //circle.SetRadius(10);
   //shapes[2] = circle;
 
   // 使用されていない要素にNULLを設定
   shapes[2] = NULL;
 
   // CSquareオブジェクトを作成し、shapes[3]にそのポインタを書き込む
   CSquare *square = new CSquare();
   square.SetSide(5);
   shapes[3] = square;
 
   // CSquareオブジェクトを作成し、shapes[4]にそのポインタを書き込む
   square = new CSquare();
   square.SetSide(10);
   shapes[4] = square;
 
   // ポインタ配列のサイズを取得する
   int total = ArraySize(shapes);

   // 配列の全ポインタをループ
   for( int i=0; i < 5; i++ ) {

      // 指定されたインデックスのポインタが有効の場合
      if ( CheckPointer( shapes[i]) != POINTER_INVALID ) {

         // オブジェクトのタイプと面積をログ出力
         PrintFormat( " オブジェクトタイプ: %d , 面積: %G",
                     shapes[i].GetType(), shapes[i].GetArea()
                    );
      } else {
         // ポインタタイプがPOINTER_INVALIDの場合

         // エラーログ出力
         PrintFormat("オブジェクトshapes[%d]は初期化されていません。 ポインタは%s",
                     i , EnumToString(CheckPointer(shapes[i]))
                    );
      }
   }
 
   // 作成した動的オブジェクトは削除する必要があります。
   for( int i=0; i < total ;i++) {

      // ポインタタイプがPOINTER_DYNAMICのオブジェクトのみ削除可能
      if ( CheckPointer(shapes[i]) == POINTER_DYNAMIC ) {
         // 削除ログ出力
         PrintFormat("オブジェクトshapes[%d]を削除",i);

         // ポインタのオブジェクトを削除
         delete shapes[i];
      }
   }
}



delete演算子を使用してオブジェクトを削除する時は注意する必要があります。
そのポインタのタイプをチェックする必要があります。
POINTER_DYNAMICポインタを持ったオブジェクトのみdelete演算子で削除する事が出来ます。
他タイプのポインタの場合はエラーが返されます。

しかし、継承時の関数の再定義に加えて、ポリモーフィズムもまたクラス内の異なるパラメータセット持った同じ関数が実装が含まれます。
これは同じ名前の複数の関数を持ったクラスを意味します、しかしパラメータセットと型が異なります。
この場合、ポリモーフィズムは関数のオーバーロードを介して実装されます。
スポンサーリンク



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


Top

inserted by FC2 system