実行時型識別(Runtime Type Identification;RTTI)

typeid演算子・type_info

実行時に型を調べるには演算子typeidを使う.

if (typeid(obj1) == typeid(obj2)) {
    ...
}

typeidは,type_info型のリファレンスを返すようになっている. type_info型は次のメソッドおよび演算子をもっていて, 上の例ではtype_info::operator==(const type_info&)が呼ばれて同じ方かどうかを判定するという仕組みになっている. type_infoは,ヘッダーファイルtypeinfoで定義されている. クラス名はtype_infoとアンダーバーが間にはいっているけど, ヘッダーファイル名はtypeinfoとアンダーバーがはいらないことに注意.

メソッド 動作
== 同じ型かどうか判定する.
!= 異なる型かどうか判定する(==のNOT).
before(rhs) 処理系が使用しているオブジェクト配置順位(collation order)で, *thisオブジェクトがrhsより前に並んでいるかどうかを判定する. いったいどういう用途で使うのか(かつ無害な使い方があるのか)疑問なメソッド….
name() オブジェクトの型を表現する文字列(たとえば“class C”みたいな文字列)へのポインタを返す. どんな文字列が返ってくるかは実装依存なので,name()が返してくる文字列をみて動作を変えるようなプログラムは好ましくない.

type_infoのクラス定義はだいたい次のような感じになっていると思われる.

class type_info {
public:
    virtual ~type_info();
    bool operator==(const type_info& rhs) const;
    bool operator!=(const type_info& rhs) const;
    bool before(const type_info& rhs) const;
    const char* name() const;
private:
    type_info(const type_info&);
    const type_info& operator==(const type_info&);
};

参考:実行時型識別の注意点

基本クラスのポインタとtypeidを組み合わせて使い場合には少し気をつけなければならないポイントがある. たとえば基本クラスBからクラスDが派生している場合, B* obj = new D();のように,基本クラスBのポインタが, 派生クラスDのインスタンスをさしているというような場合がある. この場合,typeid(*obj)は何を返すべきだろう? たとえば,typeid(*obj).name()としたら,“class B”になるべきか,“class Dになるべきか? 正解は“class B”で,たとえ全く同じインスタンスをさしているポインタ同士でも, それぞれのポインタの型が違っていれば違うものと判定されてしまう. 直感的にはインスタンスの型を判定してほしいと思うんだけども….

たとえば,次のソースコードでは,typeid(*pobj1) == typeid(*pobj2)は 同じインスタンスをさしているのにfalseになってしまう.

class B { };
class D : public B { };

int main(void)
{
    D* pobj1 = new D();
    B* pobj2 = pobj1;

    if (typeid(*pobj1) == typeid(*pobj2)) // will be false
        cout << "same" << endl;
    else
        cout << "different" << endl;

    return 0;
}
コード 結果 備考
typeid(*pobj1).name() class D
typeid(*pobj2).name() class B まったく同じモノをさしているのにB*経由だとclass Bになる. オブジェクトの本当の型(class D)が返ってくる仕様のほうが自然じゃないかと思う人もいるのでは?
typeid(pobj1).name() class D * おまけ(ここでの議論とはかんけいありません)

もちろん,この動作が正しくないわけではなくて,typeidで型を判定したのち, 型に応じてメソッドを呼び出すようなプログラムを書く場合, この動作になってくれないと困るだろう(結局B*の変数の場合,Bのもっているメソッドだけが呼び出せるわけだから). ただ,この意味で使う目的ならtypeid演算子って,むしろ廃止したほうがよかったのでは?とか思ったりもする….


はたいたかし
2001-07-21
トップ > 開発ツール > C++