この文書の現在のバージョンと選択したバージョンの差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
ja:cpp:class [2017/05/13 09:36] kota [覚書] |
ja:cpp:class [2017/05/28 18:09] (現在) kota [覚書] |
||
---|---|---|---|
ライン 4: | ライン 4: | ||
===== 覚書 ===== | ===== 覚書 ===== | ||
- | * 親クラスでコンストラクタの中で仮想関数を使うのは危険 | + | 以下親クラス-子クラス、基底クラス-派生クラスという言葉がでてくるが、親<->基底、子<->派生で読み換え可。(混じってるだけ) |
- | * 継承した子クラスのコンストラクタが呼ばれた時、親クラスのコンストラクタ->子クラスのコンストラクタという順で呼ばれる。 | + | === 継承とvirtual === |
- | * 親クラスのコンストラクタで仮想関数が呼ばれる時、子クラスのコンストラクタはまだ呼ばれていないので、親クラスの関数が呼ばれることになる。 | + | * 継承関係にある Base, Derived の2クラスが、同じ名前の関数func()を持っているとする。 |
- | * つまり、子クラスでオーバーライドしているつもりの関数は呼ばれない。 | + | * Base *obj = new Derived()としたとき(ポリモーフィズム) |
- | * 親クラスで純粋仮想関数なんかにしておいた場合は定義されていない関数になるので、コンパイル(リンク)に失敗するはず。 | + | * func()が親クラスでvirtualで宣言されている時 : Derived::func() が呼び出される |
- | * ので、親クラスの中で呼ぶのではなく、子クラスのコンストラクタの中で呼び出すべき。 | + | * func()が親クラスでvirtualで宣言されていない時 : Base::func() が呼び出される |
- | * Initialize() みたいな関数を作った時など注意したほうがいいかも。 | + | * ポリモーフィズムを利用する場合は大概、派生クラスの関数のほうが呼ばれて欲しいだろうので、virtualで宣言すべきだろう |
+ | * virtualでない場合、例えば親クラスの関数(子クラスでは再定義されていない)でfunc()を呼び出している場合、Derived *obj = new Derived() のようにしていたとしても、(親クラスの世界の中でfunc()をよびだしているので)Base::func()が呼ばれてしまう。virtualで宣言しておけばDerived::func()がちゃんと呼ばれる。 | ||
+ | * あえて基底クラスのほうのfunc()を呼びたければ、スコープ解決演算子を使ってBase::func() と明示的に指定して呼び出せばよい。 | ||
+ | |||
+ | |||
+ | === 親クラスでコンストラクタの中で仮想関数を使うのは危険 === | ||
+ | * 継承した子クラスのコンストラクタが呼ばれた時、親クラスのコンストラクタ->子クラスのコンストラクタという順で呼ばれる。 | ||
+ | * 親クラスのコンストラクタで仮想関数が呼ばれる時、子クラスのコンストラクタはまだ呼ばれていないので、親クラスの関数が呼ばれることになる。 | ||
+ | * つまり、子クラスでオーバーライドしているつもりの関数は呼ばれない。 | ||
+ | * 親クラスで純粋仮想関数なんかにしておいた場合は定義されていない関数になるので、コンパイル(リンク)に失敗するはず。 | ||
+ | * ので、親クラスの中で呼ぶのではなく、子クラスのコンストラクタの中で呼び出すべき。 | ||
+ | * Initialize() みたいな関数を作った時など注意したほうがいいかも。 | ||
+ | |||
+ | === (仮想メンバ関数をもつ)親クラスのでデストラクタはvirtualでなくてはならない=== | ||
+ | * ポリモーフィズムを利用する(Parent *obj = new Daughter() のように使う)ことを想定 | ||
+ | * デストラクタが virtual でない場合、親クラスの型のポインタを delete した際には親クラスのデストラクタしか呼ばれない | ||
+ | * 上の括弧内の例では、deleteした時に Parent *obj としているParent Classのデストラクタがまず呼ばれ、Daughter Classのデストラクタは呼ばれない | ||
+ | * virtualをつけておくと、先に子クラスのデストラクタが呼ばれ、続いて親クラスのデストラクタが呼ばれ、結果両方のデストラクタが呼ばれることになる | ||
+ | * コンストラクタは”下(親、基底クラス)から順に”構築していくのに対し、デストラクタはその逆順でオブジェクトを破壊していく、だそう。 | ||
+ | * 基本的には継承される親クラスのデストラクタにはvirtualをつけておいたほうがよい | ||