C++では、多態性を実現するかしないかによってオーバーライドの方法が変わります。
目次
単純なオーバーライド
C++では派生クラス側で基底クラスと同名の関数を定義することで、自動的にオーバライドが実現されます。
struct Animal { void say() { puts("・・・"); } };
struct Cat : Animal { void say() { puts("にゃー"); } };
struct Dog : Animal { void say() { puts("ワン!"); } };
Animal().say(); // "・・・"
Cat().say(); // "にゃー"
Dog().say(); // "ワン!"
ただし、派生クラスを基底クラスのポインタにキャストした状態でメンバ関数を呼び出すと、基底クラス側の関数が呼び出されてしまう点に注意して下さい。
Animal *animal = new Dog();
animal->say(); // "・・・"
派生クラス側のメンバ関数が呼び出されるという、ポリモーフィックな挙動を実現したい場合には、仮想関数の利用が必要となります。次項を参考にして下さい。
ポリモーフィズムを実現するためのオーバライド
C++でポリモーフィズム(多態性)を意識したオーバーライドを実現するためには、基底クラス側のオーバライド対象の関数にvirtual
指定子を指定する必要があります。なお、virtual指定子によってオーバーライドが可能になった関数は仮想関数と呼ばれます。
struct Animal { virtual void say() { puts("・・・"); } };
struct Cat : Animal { void say() { puts("にゃー"); } };
struct Dog : Animal { void say() { puts("ワン!"); } };
Animal *animal = new Dog;
animal->say(); // "ワン!"
仮想関数のオーバーライドによって、抽象メソッドのようなポリモーフィックな挙動を実現することも可能となります。
struct Animal {
void say() { puts(makeSound()); }
virtual const char* makeSound() { return "・・・"; };
};
struct Cat : Animal {
const char* makeSound() { return "にゃー"; }
};
struct Dog : Animal {
// const char* makeSound() { return "ワン!"; }
};
Cat().say(); // "にゃー"
Dog().say(); // "・・・"
ちなみに、オーバーライド時、派生クラス側の関数ではvirtualキーワードの省略が可能になります。ただし、闇雲に省略せず、対象の派生クラスが更に別のクラスから継承されるようなケースを想定してきちんと明示するのがベストです。
override指定子
C++11以降のC++ではオーバーライドの際にoverride指定子を明示することができます(必須ではなく任意)。
struct Animal {
virtual void say() { puts("・・・"); }
};
struct Cat : Animal {
void say() override { puts("にゃー"); }
};
overrideを指定した関数が非仮想関数や未定義の関数だった場合にはコンパイルエラーが発生します。
struct Cat : Animal {
// エラー: Only virtual member functions can be marked 'override'
void CRY() override { puts("にゃー"); }
};
final指定子
final指定子はオーバーライドを禁止するためのキーワードです。例えば「サブクラス(Cat)のサブクラス(Nyan)にはオーバーライドを許可したくない」といったケースで利用されます。
struct Cat : Animal {
// Catのサブクラスにはオーバーライドを許可しないことを明示する
void say() final { puts("にゃー"); }
};
struct Nyan : Cat {
// 親クラスでfinalが指定されているためオーバーライドできない
// エラー: Declaration of 'say' overrides a 'final' function
void say() { puts("にゃん"); }
};