RTTI (Run-Time Type Information) 提供 C++ 程式在運行時辨識物件動態類型的能力。雖然專家建議優先使用虛擬函數實現多態,但在異質容器、框架基類 (如 MFC CObject) 等場景,動態類型判斷不可或缺。本文系統介紹 typeid、dynamic_cast 兩大核心運算子的用法、效能差異與最佳實踐。
編譯器預設可能關閉 RTTI (如 MSVC /GR-) 以節省空間/速度。使用前必須開啟:
-fno-rtti 可關閉標頭檔:#include <typeinfo>
class File { // 抽象基類
public:
virtual int Open(const string& filename) = 0;
virtual int Close(const string& filename) = 0;
virtual ~File() = 0; // 純虛擬解構函數
};
class DiskFile : public File {
public:
int Open(const string& filename) override;
int Close(const string& filename) override;
virtual int Flush();
virtual int Defragment();
};
class TextFile : public DiskFile {
public:
int SortByWords();
};
class MediaFile : public DiskFile {
// 音訊/視訊檔案
};
class LocalizedMedia : public MediaFile {
// 多語言說明的媒體檔
};
File* pFile;
if (someCondition)
pFile = new TextFile; // 動態類型: TextFile
else
pFile = new DiskFile; // 動態類型: DiskFile
// 靜態類型皆為 File*
#include <typeinfo>
void BuildMenu(const File* pFile)
{
if (typeid(*pFile) == typeid(TextFile))
AddOption("Edit");
else if (typeid(*pFile) == typeid(MediaFile))
AddOption("Play");
}
新增 LocalizedMedia 繼承 MediaFile 時,typeid(*pFile) == typeid(MediaFile) 為 false,需修改 BuildMenu 添加判斷,違反開放封閉原則。
void BuildMenu(const File* pFile)
{
// 檢查是否為 MediaFile 或其衍生類
if (dynamic_cast<const MediaFile*>(pFile))
{
AddOption("Play"); // 包含 LocalizedMedia
}
else if (dynamic_cast<const TextFile*>(pFile))
{
AddOption("Edit");
}
}
dynamic_cast<T*>(p):若 p 指向 T 或其衍生類物件,回傳轉型後指標;否則回傳 nullptrdynamic_cast<T&>(ref):參考版本,失敗拋出 std::bad_cast 異常新增 LocalizedMedia : MediaFile 時,dynamic_cast<MediaFile*> 自動匹配,**無需修改既有判斷邏輯**。
| 層面 | typeid | dynamic_cast |
|---|---|---|
| 時間複雜度 | O(1) 常數時間 | O(h) 類別階層深度 (需遍繼承鏈) |
| 語義 | 精確類型相等 | 「是否為...或其子類」 (is-a 關係) |
| 適用場景 | 需精確比對、效能敏感 | 多態容器、框架擴充、is-a 判斷 |
| 失敗行為 | 回傳 false | 指標版回傳 nullptr, 參考版拋出異常 |