はじめに
C++の継承とポリモーフィズム(多態性)を使うと、親クラスへのポインタの配列に、様々な種類の子クラスオブジェクトをまとめて格納できます。しかし、時には「このポインタが今指しているのは、『戦士』オブジェクトなのか、それとも『魔法使い』オブジェクトなのか」を、プログラムの実行中に正確に知りたい場面があります。
このような、プログラム実行時にオブジェクトの型情報を取得するための仕組みが「実行時型情報 (Run-Time Type Information, RTTI)」です。C++では、<typeinfo>
ヘッダーをインクルードし、typeid
演算子を使うことで、このRTTIの機能を利用できます。
この記事では、typeid
を使って、親クラスへのポインタが指す先の、実際のオブジェクトの型を判別する方法を解説します。
typeid
を使った型判別のサンプルコード
このコードは、Character
(親クラス)へのポインタが、Warrior
と Mage
という2種類の子クラスオブジェクトのどちらを指しているかを、typeid
を使って判別します。
完成コード
#include <iostream>
#include <string>
#include <typeinfo> // typeid を使うために必要
using namespace std;
// --- 親クラス(基底クラス) ---
// RTTIを有効にするには、基底クラスに少なくとも1つの仮想関数が必要
class Character {
public:
string name;
Character(string n) : name(n) {}
virtual ~Character() {} // 仮想デストラクタ
};
// --- 子クラス(派生クラス) ---
class Warrior : public Character {
public:
Warrior(string n) : Character(n) {}
};
class Mage : public Character {
public:
Mage(string n) : Character(n) {}
};
// ポインタが指す先の型を判別して表示する関数
void printCharacterType(const Character* p_char) {
if (!p_char) return;
// 1. typeidでポインタが指す先のオブジェクト(*p_char)の型を調べる
const type_info& info = typeid(*p_char);
if (info == typeid(Warrior)) {
cout << p_char->name << " は、ウォリアーです。" << endl;
}
else if (info == typeid(Mage)) {
cout << p_char->name << " は、メイジです。" << endl;
}
else if (info == typeid(Character)) {
cout << p_char->name << " は、一般キャラクターです。" << endl;
}
else {
cout << p_char->name << " は、不明な職業です。" << endl;
}
}
int main() {
Warrior warrior("戦士");
Mage mage("魔法使い");
Character* p_character1 = &warrior;
Character* p_character2 = &mage;
printCharacterType(p_character1);
printCharacterType(p_character2);
return 0;
}
コードの解説
RTTIを有効にするための条件
typeid
を使ってポリモーフィックな(多態的な)型判別を行うには、親クラスに少なくとも一つ以上の仮想関数が定義されている必要があります。
virtual ~Character() {}
: デストラクタを仮想関数(仮想デストラクタ)にするのが、最も一般的で安全な方法です。これにより、delete
経由でのデストラクタ呼び出しも正しく行われるようになります。
<typeinfo>
ヘッダーと typeid
演算子
#include <typeinfo>
:typeid
を利用するために、このヘッダーファイルをインクルードする必要があります。typeid(*p_char)
:typeid
演算子は、引数として渡されたオブジェクトや式の「型情報(type_info
オブジェクト)」を返します。- ポインタ変数
p_char
そのものではなく、*p_char
のように間接参照して、ポインタが指し示している先のオブジェクトを渡すのがポイントです。
- ポインタ変数
typeid(Warrior)
:typeid
には、Warrior
のような型名を直接渡すこともできます。==
での比較:typeid
が返すtype_info
オブジェクト同士を==
で比較することで、型が完全に一致するかどうかを判定できます。
まとめ
今回は、C++の実行時型情報(RTTI)と typeid
演算子を使って、オブジェクトの実際の型を判別する方法を解説しました。
- 親クラスに仮想関数を定義することで、RTTIが有効になる。
<typeinfo>
ヘッダーをインクルードし、typeid
演算子を使う。typeid(*ポインタ) == typeid(クラス名)
のように比較して、型を判別する。
typeid
による型判別は便利ですが、多用すると、それは仮想関数を使ったポリモーフィズムで解決すべき問題かもしれません。if-else
の長い連鎖が生まれるようなら、設計を見直す良い機会です。オブジェクト指向の原則を念頭に置き、適切な場面で活用しましょう。