3-4. ポリモーフィズム

公開日: 16:22 3. オブジェクト指向編/3-4. ポリモーフィズム


ポリモーフィズムという言葉はたくさんの形を持つことを意味します。一般的に、ポリモーフィズムはクラスの階層や継承によって関連している時に出てきます。

C++のポリモーフィズムはメンバ関数の呼び出しが、その関数を呼ぶオブジェクトの型によって異なる関数を実行することを意味します。

基底クラスから2つの派生クラスができる例を考えてみましょう。
#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      int area()
      {
         cout << "親クラスの面積 :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "クラスRectangleの面積 :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "クラスTriangleの面積 :" <<endl;
         return (width * height / 2); 
      }
};
// プログラムの開始
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);

   // Rectangleのアドレスを保存
   shape = &rec;
   // call rectangleのareaを呼び出し
   shape->area();

   // Triangleのアドレスを保存
   shape = &tri;
   // triangleのareaを呼び出し.
   shape->area();
   
   return 0;
}

上のコードをコンパイルし実行すると、次の結果が得られます。
親クラスの面積 :
親クラスの面積 :

間違った出力の原因はarea()関数の呼び出しがコンパイラによって基底クラスで定義されたバージョンにセットされたからです。これは関数呼び出しの静的解決、あるいは関数呼び出しはプログラムが実行される前に修正されるため、静的リンケージと呼ばれます。また、これらはarea()関数がプログラムのコンパイル中にセットされるため事前バインディングとも呼ばれます。

Shapeクラス内のarea()関数の宣言の前にvirtualキーワードをつけてプログラムに少しの修正を加えて見ましょう。
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "親クラスの面積 :" <<endl;
         return 0;
      }
};
この変更後、前のコードをコンパイルし実行すると、次の結果が得られます。
クラスRectangleの面積 :
クラスTriangleの面積 : 

今回は、コンパイラは型ではなくポインタの中身を調べています。これは、クラスtriangeとrectangleのオブジェクトのアドレスは*shapeに保存されており、それぞれのarea()関数が呼び出されるためです。

これまで見てきたように、それぞれの子クラスは分離した実装のarea()関数を持っています。これはポリモーフィズムの一般的な使われ方です。異なるクラスで同じ名前を関数があり、引数も同じですが、異なる実装を持ちます。

仮想関数

仮想関数は基底クラスの関数であり、virtualキーワードを使って宣言されます。基底クラスの仮想関数の定義と派生クラスで別バージョンの関数を定義すると、コンパイラにその関数では静的リンケージを使いたくないことを知らせます。 

やりたいことは、プログラムのある場所で呼び出される関数の選択が呼び出されるオブジェクトの種類に基づいていることです。この種の操作は動的リンケージ、または遅延バインディングと呼ばれます。

純粋仮想関数

基底クラスに仮想関数を含めることができるので、派生クラスでそのクラスのオブジェクトに合うよう再定義することになりますが、基底クラスの関数での定義は意味があり ません。 基底クラスのarea()関数を次のように変更します。
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      // 純粋仮想関数
      virtual int area() = 0;
};
"=0"はコンパイラに関数は中身が無いことを伝え、上のような関数は純粋仮想関数と呼ばれます。
  • ?±??G???g???[?d????u?b?N?}?[?N???A

0 件のコメント :

コメントを投稿