抽象クラス

ふわふわ〜


virtual

メンバ関数には、virtual という指定ができる。こんな関数を仮想関数っていう。

class A {
  virtual void a() {}
};

オーバーライド

クラスを派生するときに、派生元クラスと同じ形のメンバ関数を定義して上書きできる。

試しに、普通の関数を上書きしたクラスを用意して、そのオブジェクトを親クラスの型にぶち込んでみる。

struct A { void a() { std::cout << "A\n"; } };
struct B : public A { void a() { std::cout << "B\n"; } };

B b;
A &ref = b;
ref.a(); // A

うん。オブジェクトは struct B のものなのに、struct A のメンバ関数を呼び出している。

これは、型が struct A になってるからメンバもそっちなんだと解決される。

仮想関数でも同じことをやってみよう。

// virtual の指定は派生元だけで OK
struct A { virtual void a() { std::cout << "A\n"; } };
struct B : public A { void a() { std::cout << "B\n"; } };

B b;
A &ref = b;
ref.a(); // B

あれ、struct B のメンバ関数が呼ばれた。

つまり、仮想関数にすると、実際のオブジェクトのメンバ関数が呼ばれるわけだ。

この定義の上書きを オーバーライド って言う。

ちなみに、override を付けるとオーバーライドできていないときにコンパイラがエラーを出してくれる。

struct A {
  virtual void a() { std::cout << "A\n"; }
};
struct B : public A {
  void a() override { std::cout << "B\n"; }
};

純粋仮想関数

仮想関数を定義するときに、定義の部分を = 0 ってすると 純粋仮想関数 になる。

class A {
  virtual void a() = 0;
};

純粋仮想関数が含まれるクラスは 抽象クラス として扱われるよ。

抽象クラスは、オブジェクトを作ることができなくなるよ。

A a; // エラー

でも、参照やポインタを扱うことはできる。

void invoke(A &ref) {
  ref.a();
}

さっきみたいに、仮想関数をオーバーライドして使うのが前提になってるんだ。