C++ポリモーフィズム

ポリモーフィズム (polymorphism:多様性)について

ポリモーフィズムの概念

ポリモーフィズムとは、メソッドに複数の(多様な)振る舞いを対応させるというオブジェクト指向の概念です。
つまり、同じメッセージを送信した際に、メッセージを受け取ったインスタンスが、そのクラスによって違う振る舞いをすることです。
ポリモーフィズムは、同じメソッド名で場面に応じた動作をさせることができるため、オブジェクトを呼び出す側が意識しなくても実行時の条件に合った適切なメソッドが実行できるようになります。


C++におけるポリモーフィズムとは

C++におけるポリモーフィズムとは、同じ名前を使って異なる実装の関数にアクセスできる仕組みです。
つまり、一つの名前で二つ以上の関連する目的に使用できる仕組みといえます。
C++では、「継承」と「仮想関数」を利用することで、ポリモーフィズムを実現します。


ポリモーフィズムの実装

継承の利用について

継承を利用することで、基底クラスへのポインタは派生クラスを指すことができます。
(なお、派生クラスのポインタで基底クラスのオブジェクトを指すことはできません。)


class Base{ ... };
class Derived : public Base{ ... };

Base *ptr;
Derived object;
ptr = &object;

仮想関数について

仮想関数とは、基底クラスで「virtual」宣言され、派生クラスで再定義される関数です。
ポインタが指すオブジェクトを変更することで、振る舞いを変更することができます。


virtual void function(int argument){
    // 関数の定義
}

ポリモーフィズムの実現

基底クラスのポインタが派生クラスのオブジェクトを指している状態で仮想関数speak()を呼び出すと、オブジェクトの関数speak()を呼び出します。


#include <iostream>

class Human{  // 基底クラス
public:
    virtual void speak(){ printf("I am human.\n"); } // 仮想関数
};

class Girl : public Human{
public:
    void speak(){ printf("I am girl.\n"); }
};

class Boy : public Human{
public:
    void speak(){ printf("I am boy.\n"); }
};

int main(void)
{
    Human *ptr;
    Girl  alice;
    Boy   bob;

    // I am human.は出力されない。
    ptr=&alice; ptr->speak(); // I am girl.
    ptr=&bob;   ptr->speak(); // I am boy.

    return(0);
}

純粋仮想関数について

基底クラスの仮想関数の呼び出しを禁止するには、「純粋仮想関数」を用いることで対応できます。
純粋仮想関数を持つクラスは「抽象クラス」となり、オブジェクトの生成ができなくなります。
これにより、「派生クラスで必ず対応するメソッドを定義しなければならない」というルールを定めることができます。

純粋仮想関数を定義するには、下記のように「関数に0を代入する」という形式で記述します。


virtual void func() = 0; // 純粋仮想関数の定義

RTTI(Run-Time Type Identification:実行時型識別)

RTTIとは

RTTIとは、オーバーライドされた仮想関数の型を取得する機能です。
ポリモーフィズムの利用に際して、基底クラスのポインタがどの型のオブジェクトを指しているかコンパイル時に決定できない場合に使用します。

実行時型情報を得るには「typeid」演算子を用います。
なお、typeid演算子を利用するにはtypeinfoをインクルードする必要があります。


#include <iostream>
#include <typeinfo>

class Human{  // 基底クラス
public:
    virtual void speak() = 0;
};

class Girl : public Human{
public:
    void speak(){ printf("I am girl.\n"); }
};

class Boy : public Human{
public:
    void speak(){ printf("I am boy.\n"); }
};

int main(void)
{
    Girl  alice;
    Boy   bob;
    Human *ptr[] = {&bob, &alice};

    for(int i=0; i<2; i++){
        // 型の名前を出力する
        printf("%s\n", typeid(*ptr[i]).name());

        // 型がGirlならばメソッドを実行する
        if(typeid(*ptr[i]) == typeid(Girl)) ptr[i]->speak();
    }

    return(0);
}

関連ページ