| 特性 | dynamic_cast | DYNAMIC_DOWNCAST (MFC 宏) |
|---|---|---|
| 所屬體系 | C++ 標準語言特性 (RTTI) | MFC 框架巨集 (afx.h) |
| 適用對象 | 多態類層次 (含虛函數) | 繼承自 `CObject` 的 MFC 類 |
| 運行時機制 | 編譯器生成 RTTI 信息查詢 | MFC 運行時類信息 (`CRuntimeClass`) |
| 失敗返回 | 指針轉換返回 `nullptr`,引用轉換拋 `bad_cast` | 返回 `NULL` |
| 跨模塊 (DLL) 行為 | 需編譯器/鏈接器支持 RTTI 導出 | MFC 擴展 DLL 需 `AFX_EXT_CLASS` 正確導出類信息 |
| 性能開銷 | 極低 (通常一次虛表查找 + 類型比較) | 類似,額外巨集展開開銷可忽略 |
| 編譯依賴 | 需啟用 RTTI (`/GR` / `-frtti`) | 需包含 ` |
dynamic_cast(expression)
type-id:目標類型(類指針、類引用、`void*`)expression:源表達式(指針/引用/`void*`)class Base { virtual ~Base() = default; };
class Derived : public Base { void derivedOnly() {} };
class Another : public Base {};
// 1. 下行轉換 — 最常用
Base* pb = new Derived();
Derived* pd = dynamic_cast(pb); // 成功,非空
if (pd) pd->derivedOnly();
// 2. 交叉轉換 — 派生類之間
Another* pa = dynamic_cast(pb); // 失敗,返回 nullptr
// 3. 上行轉換 — 隱式轉換即可,dynamic_cast 多餘但合法
Base* pb2 = dynamic_cast (pd); // 等價於隱式轉換
// 引用形式 (失敗拋異常)
try {
Derived& rd = dynamic_cast(*pb); // 成功
Another& ra = dynamic_cast(*pb); // 拋 std::bad_cast
} catch (const std::bad_cast&) { /* 處理 */ }
**基類必須至少有一個虛函數**(通常是虛析構函數),否則編譯錯誤:cannot dynamic_cast 'pb' (of type 'class Base*') to type 'class Derived*' (source type is not polymorphic)
#define DYNAMIC_DOWNCAST(class, pointer) \
(class*)((pointer) && (pointer)->IsKindOf(RUNTIME_CLASS(class)) ? (pointer) : NULL)
// MFC 風格類定義
class CMyBase : public CObject {
DECLARE_DYNAMIC(CMyBase)
public:
virtual ~CMyBase() {}
};
IMPLEMENT_DYNAMIC(CMyBase, CObject)
class CMyDerived : public CMyBase {
DECLARE_DYNAMIC(CMyDerived)
public:
void DerivedOnly() {}
};
IMPLEMENT_DYNAMIC(CMyDerived, CMyBase)
// 使用
CMyBase* pBase = new CMyDerived();
CMyDerived* pDerived = DYNAMIC_DOWNCAST(CMyDerived, pBase);
if (pDerived) pDerived->DerivedOnly();
// pDerived 為 NULL 表示轉換失敗
**唯一選擇:`dynamic_cast`**。現代 C++ 推薦:
**統一用 `DYNAMIC_DOWNCAST`**:
| 對象類型 | 推薦轉換 | 理由 |
|---|---|---|
| 繼承 `CObject` 的 MFC 類 | `DYNAMIC_DOWNCAST` | 框架一致性、序列化支持 |
| 非 MFC 多態類 | `dynamic_cast` | 標準語法、無額外依賴 |
| 跨體系轉換 | **禁止** / 重構設計 | 無公共運行時類型系統,極易出錯 |
// 1. 訪問者模式 — 雙分發替代類型查詢
struct Visitor { virtual void visit(DerivedA*) = 0; virtual void visit(DerivedB*) = 0; };
struct Base { virtual void accept(Visitor&) = 0; };
struct DerivedA : Base { void accept(Visitor& v) override { v.visit(this); } };
struct DerivedB : Base { void accept(Visitor& v) override { v.visit(this); } };
// 2. std::variant + std::visit (C++17) — 代數數據類型
using Shape = std::variant;
void draw(const Shape& s) { std::visit([](auto&& shape) { shape.draw(); }, s); }
// 3. 型別擦除 — 統一接口隱藏具體類型
class Drawable {
struct Concept { virtual void draw() = 0; virtual ~Concept() = default; };
template struct Model : Concept { T obj; void draw() override { obj.draw(); } };
std::unique_ptr p;
public:
template Drawable(T t) : p(std::make_unique>(std::move(t))) {}
void draw() { p->draw(); }
};
核心洞見: `dynamic_cast` 是 C++ RTTI 系統的標準化產物,`DYNAMIC_DOWNCAST` 是 MFC 獨有的運行時類信息體系映射。**選型取決於項目技術棧,而非性能**。現代 C++ 設計傾向於**靜態多態 (模板/概念)、代數數據類型 (variant)、型別擦除** 替代動態下行轉換,從根源消除運行時類型查詢需求。