このBoxクラスをpublic継承しても安全でしょうか?
1 2 3 4 5 |
class Box : public Object { public: Box(); ~Box(); }; |
正解は……この情報だけでは何とも言えない。でした。
一見するとダメっぽいと感じる人も多いかと思います。特にc++のことをちょっと詳しくなった頃の人はまずデストラクタに目が行くかと思います。
デストラクタの前にvirtualが付いていませんね。virtualで無いデストラクタは継承してはいけないということはc++で最初に学ぶことの一つです。
もう少し情報を足して、Objectクラスの定義も見てみましょう。
1 2 3 4 |
class Object { public: virtual ~Object(); }; |
シンプルです。
virtualが定義されたデストラクタが定義されている模様です。
これではっきりしました。Boxはpublic継承しても安全です。
「Boxをpublic継承しても安全」という表現をもっと詳しく書くと「Boxを継承したクラスをnewで生成して、そのポインタをBox,もしくはObjectのポインタに入れて、deleteを呼んでも安全」ということです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class MyBox : public Box { public: MyBox(); ~MyBox(); }; int main() { Box* box = new MyBox; delete box; // OK 問題なくMyBoxのデストラクタが呼ばれる Object obj = new MyBox; delete obj; // OK これも問題なくMyBoxのデストラクタが呼ばれる return 0; } |
Objectクラスのデストラクタにvirtualが付いているので、Objectをpublic継承している全てのクラスのデストラクタは暗黙にvirtual指定が付きます。
C++では基底クラスでvirtualで定義されたメソッドは、派生先でも 、もれなくvirtualになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Super{ public: Super(){} virtual ~Super(){} public: virtual void func1() {} virtual void func2() {}; virtual void func3() = 0; }; class Sub : public Super { public: Sub() {} ~Sub(){} // これは暗黙にvirtual public: void func1() {}; // これも暗黙にvirtual void func2() {}; // これも暗黙にvirtual void func3() {}; // これも暗黙にvirtual }; |
一昔前は派生先のメソッドにvirtualをつけることで、継承元のメソッドをオーバーライドしてますよ的なことを匂わせるアプローチもありましたが今はc++11から追加されたoverrideキーワードがあるのでそちらを使いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Super{ public: Super(){} virtual ~Super(){} public: virtual void func1() {} virtual void func2() {}; virtual void func3() = 0; }; class Sub : public Super { public: Sub() {} ~Sub(){} public: void func1() override {} // オーバーライドしたことが明快 void func2() override {} // オーバーライドしたことが明快 void func3() override {} // オーバーライドしたことが明快 void func4() override {} // エラー!基底クラスにないメソッド名! }; |
オーバーライドしている意思が明確になり、より可読性の高いコードとなります。また、メソッド名を間違えたりしても、今までは間違った名前で新しいメソッドが作られるだけで不具合の温床になっていましたが、overrideキーワードの恩恵で、基底クラスに無いメソッド名を指定した場合はエラーにしてくれます。素敵ですね!
まとめ
- 基底クラスでvirtualだったものは派生先では勝手にvirtualになる
- オーバーライドを明示するためにc++11から追加されたoverrideキーワードを使おう