続・実行時のクラス判定

以前、実行時のクラス判定として、 MFCの場合に使える方法を紹介しました。 今回は、もっと汎用的な、typeiddynamic_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から派生しているとして、 変数varCMunya *型であるとき、

        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++の場合、このような機能を使うには、 「実行時型情報を使う」設定にしておく必要があります。 他の処理系でもオプションの設定等が必要かも知れません。