dynamic_cast

オーバーライド(仮想関数)の項で、派生オブジェクトポインタを基底オブジェクトポインタに格納して、基底クラスメソッドを呼び出すと派生クラスメソッド側が呼ばれるというのはすでに扱った・・・。

このように派生オブジェクトを基底オブジェクトにキャストするのをアップキャストという・・・。
基底クラスが上で派生クラスを下とすると、基底クラスに上るような変換だからだ・・・。
基底クラスに変換されるのだから、当然基底クラスのメンバしか使えないが、基底クラスが派生クラスのサブセットと考えれば、基底クラスのメンバ全てを安全に使えるとも言える・・・。

基底オブジェクトを派生オブジェクトにキャストするのをダウンキャストという・・・。
派生クラスに下りる変換・・・。
これは危険である・・・。
基底オブジェクトのインスタンスが基底クラスだった場合、基底クラスのメンバしか用意してないので、キャストした後に派生クラス独自メンバを呼び出した場合、確保されていない領域にアクセスしてしまう可能性がある・・・。
また、C言語キャストなどで、基底オブジェクトのインスタンスの型と違う派生クラス型でキャストしてしまった場合も、インスタンスに実装されていないメンバを呼び出そうとして不正なアクセスになってしまう可能性がある・・・。

こういった実行時の型情報に対応したキャスト演算子がdynamic_cast・・・。
キャスト対象のオブジェクト、ポインタ、参照のインスタンスが、指定のキャスト先クラスに変換できるのならキャストし、そうでなければNULLを返す・・・。

これは基底クラス・・・。

派生クラス1・・・。

派生クラス2・・・。
継承だけじゃなく、メソッドにvirtualも付けないと、コンパイルエラーになるので注意・・・。

それぞれのオブジェクトをポインタに入れたら、まずはアップキャストで、基底ポインタに渡す・・・。
片方はC言語キャスト、もう片方はdynamic_castを使っているが・・・。

そうしたら、今度は2つの基底ポインタを2つの派生クラスにそれぞれダウンキャストしてみる・・・。

それぞれのポインタからメソッド呼び出し・・・。
アップキャストでは指定された基底クラスclass_base *に安全に戻れるので、upcast2_ptrはNULLにはならない・・・。

ではダウンキャストはどうだろうか・・・。
downcast1b_ptrはインスタンスがclass_derived2なのに対し、ダウンキャストはclass_derived1・・・。
downcast2a_ptrはインスタンスがclass_derived1なのに対し、ダウンキャストはclass_derived2・・・。
この2つはどちらもインスタンスと変換先が違うので、変換できずNULLが返ってくる・・・。
downcast1a_ptr、downcast2b_ptrは、インスタンスと変換先が同じなので、キャストできる・・・。

$ vi dynamic_cast.cpp
$ g++ -o dynamic_cast dynamic_cast.cpp base.cpp derived1.cpp derived2.cpp
$ ./dynamic_cast
base_func
derived1_func
derived2_func
base_func
base_func
dynamic_cast success!

derived1_func
1a success!
1b failed!
2a failed!
derived2_func
2b success!
$

このように、1bと2aはdynamic_castに失敗・・・。
1a, 2bは成功・・・。

Sample/cpp/dynamic_cast/dynamic_cast/src/dynamic_cast at master · bg1bgst333/Sample · GitHub