以前、実行時のクラス判定として、 MFCの場合に使える方法を紹介しました。 今回は、もっと汎用的な、typeidや dynamic_castを使う方法を紹介します。
ここでは概要しか紹介しませんので、実際に使ってみる場合にはコンパイラーの 付属文書等もご覧ください。
まずは、型を文字列として取得する方法。変数varがあったとすると、
const char * ty = typeid(var).name();
という具合に書くことができます。tyには、 "class CHoge *"などといった具合に 型名(ここではクラス名)が入ります。
もっとも、型が想定通りであるか確認するのに
assert(strcmp(ty, "class CHoge *") == 0);
と書くのはいかがでしょうか。もっとこみいった型で、文字列での書き表し方が 幾通りもあるような場合に、ちょっと不安になります。 トレース出力用などに限定して使う方がいいと思います。
変数varが本当に型CHoge *であるかどうか 確かめたい場合は、次のようにします。
assert(typeid(var) == typeid(CHoge *));
typeid()は、type_info型を返す 函数のように思って使うことができます。 typeid()の引数には変数も型も与えることができる点では 普通の函数と異なります。この性質はsizeofにも似ていますね。
クラスCMunyaがCHogeから派生しているとして、 変数varがCMunya *型であるとき、
assert(typeid(var) == typeid(CHoge *));
は失敗します。varはCMunya *であると同時にCHoge *でもある筈なんですが、 type_infoの==はそういう風にはなっていないんですね。
こんなときは次のように書きます。
assert(typeid(var).before(typeid(CHoge *)));
つまり、type_info::before()が派生関係を調べてくれる訳です。
int func(CHoge * hoge) { CMunya * munya = dynamic_cast<CMunya *>(hoge); if (munya != NULL) { munya->m_func(); } }
この例では、func()の引数はCHoge *型ですが、実際に呼び出されるときには そこから派生したCMunya *型の変数が渡されるかも知れないと想定しています。 単純化した例なので、func(CMunya *)と多重定義するという方法もありそうですが、 諸般の事情(過去とのしがらみ?)でそうできないとします。
従来ならば、単にキャストを使って、
CMunya * munya = (CMunya *) hoge;
としていたでしょう。但し、hogeがCMunya *でない場合にはCMunyaのメンバー函数を 呼び出した時点でおかしなことになりますから、何か別の手段で検査する 必要があります。
dynamic_castを使う方法だと、キャストが正当でないならば NULLになるので、より安全になります。
MSVC++の場合、このような機能を使うには、 「実行時型情報を使う」設定にしておく必要があります。 他の処理系でもオプションの設定等が必要かも知れません。