19:オーバーライドとポリモーフィズム

はじめに

C++では、親クラスで定義した関数を子クラスで「上書き」できる仕組みがあります。これをオーバーライドと呼びます。さらに、親クラス型のポインタや参照を通じて、実際には子クラスごとの動作を呼び分けられる性質を ポリモーフィズム(多態性) といいます。

ゲーム開発では「敵キャラごとに異なる攻撃方法を持たせる」などの場面で活用されます。


オーバーロードとオーバーライドの違い

混同しやすいですが、この2つは全く別の仕組みです。 オーバーロード(overload):同じクラス内で、同じ名前でも「引数の型や数」が異なる関数を複数定義すること。関数を使いやすくする工夫。同名関数の引数違いの定義。 オーバーライド(override):親クラスの関数を子クラスで上書きすること。仮想関数 virtual を使い、動作を差し替える。子クラスで関数上書き。


基本構文

class 親クラス {
public:
    virtual void 関数名();
};

class 子クラス : public 親クラス {
public:
    void 関数名() override; // 上書き
};

例:ゲームの敵キャラクター

// 19_enemy_override.cpp
#include <iostream>
using namespace std;

class Enemy {
public:
    virtual void attack() {
        cout << "敵が攻撃した!" << endl;
    }
};

class Slime : public Enemy {
public:
    void attack() override {
        cout << "スライムが体当たりしてきた!" << endl;
    }
};

class Goblin : public Enemy {
public:
    void attack() override {
        cout << "ゴブリンが棍棒を振り下ろした!" << endl;
    }
};

int main() {
    Enemy* enemies[2];
    enemies[0] = new Slime();
    enemies[1] = new Goblin();

    for (int i = 0; i < 2; i++) {
        enemies[i]->attack(); // ポリモーフィズム:同じ呼び出しで違う動作
    }

    for (int i = 0; i < 2; i++) {
        delete enemies[i];
    }

    return 0;
}

実行結果(例)

スライムが体当たりしてきた!
ゴブリンが棍棒を振り下ろした!

? 親クラス Enemy* 型でまとめても、実際には子クラスごとの動作が呼ばれる。


練習問題(19_character_override.cpp)

  1. 親クラス Characterspeak() を定義し、デフォルトでは「……」と表示する
  2. 子クラス HeroVillain (悪役)を作り、それぞれ違うセリフを speak() で表示する
  3. Character* の配列に HeroVillain を入れ、for文で speak() を呼ぶ
解答例
// 19_character_override.cpp
#include <iostream>
using namespace std;

class Character {
public:
    virtual void speak() {
        cout << "……" << endl;
    }
};

class Hero : public Character {
public:
    void speak() override {
        cout << "勇者:世界を救うのは僕だ!" << endl;
    }
};

class Villain : public Character {
public:
    void speak() override {
        cout << "魔王:この世界は我のものだ!" << endl;
    }
};

int main() {
    Character* chars[2];
    chars[0] = new Hero();
    chars[1] = new Villain();

    for (int i = 0; i < 2; i++) {
        chars[i]->speak();
        delete chars[i];
    }

    return 0;
}

まとめ