本文へスキップ
BecomeCoder

C++コース · 第11章 継承とポリモーフィズム ― 型を広げる · レッスン46

virtual ― 実行時に振る舞いを切り替える

ブラウザで完結

導入

「同じ操作でも、実際の型に応じて違う振る舞いをする」――これがポリモーフィズム(多態性)です。C++ では、親の関数virtual を付けることで実現します。オブジェクト指向のいちばんの見せ場です。

説明

親の関数に virtual を付けると、親の型のポインタ・参照から呼んでも「実際のオブジェクトの型」の関数が呼ばれます。子側には override を付けて上書きだと明示します。

flowchart TB
  ptr["Animal* p(親の型のポインタ)"]
  ptr -->|"実体が Dog なら"| d["Dog::speak() が呼ばれる"]
  ptr -->|"実体が Cat なら"| c["Cat::speak() が呼ばれる"]
#include <iostream>
#include <vector>
using namespace std;

class Animal {
public:
    virtual void speak() {       // virtual を付ける
        cout << "……" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() override {      // override で上書きを明示
        cout << "ワン!" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {
        cout << "ニャー" << endl;
    }
};

int main() {
    vector<Animal*> zoo = { new Dog(), new Cat(), new Dog() };
    for (Animal* a : zoo) {
        a->speak();   // 実際の型に応じて切り替わる(ワン!/ ニャー / ワン!)
    }
    for (Animal* a : zoo) delete a;   // 後片付け
    return 0;
}

もし virtual を付けないと、Animal* から呼んだときはすべて Animal::speak()(……)になってしまいます。virtual があるからこそ、実際の型で切り替わるのです。

まとめ

親の関数に virtual、子に override を付けると、親の型のポインタ・参照から呼んでも実際の型の関数が動きます。これがポリモーフィズムで、異なる型を同じインターフェースでまとめて扱えるようになります。

実際に動かしてみよう

下のエディタにC++を書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のC++コンパイラ(clang + libc++)がその場でコンパイルして実行し、出力が表示されます。std::string・vector・クラス・テンプレート・STL などモダンC++がそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(cin)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。