多重繼承下的指針轉換是 C++ 最易踩坑的領域之一。本文通過實測代碼,揭示 派生類對象內存佈局、靜態/動態轉換語義差異、以及 virtual 繼承 引入的虛基類指釖調整機制。
class A { virtual void f(); int i; };
class B { virtual void f(); int j; };
class C : public A, public B { int i, k; }; // 普通多重繼承
class D : virtual public A { int di; virtual void f(); };
class E : virtual public A { int ei; virtual void f(); };
class F : public D, public E { int fi; virtual void f(); } // 鑽石虛繼承
| 轉換類型 | 來源 | 目標 | 地址關係 | 備註 |
|---|---|---|---|---|
| dynamic_cast | C* | B* | 偏移 +sizeof(A) | 跟隨內存佈局調整 |
| static_cast | C* | B* | 偏移 +sizeof(A) | 編譯期固定偏移,同 dynamic_cast |
| dynamic_cast | B* | C* | 偏移 -sizeof(A) | 需 RTTI 運行時檢查 |
| static_cast | B* | C* | 偏移 -sizeof(A) | 無檢查,程序員擔保正確 |
| virtual 繼承 | F* | D*/E* | 共享唯一 A 子對象 | 引入 vbptr/vtable 指針 |
nullptr(指針)或拋出 bad_cast(引用)。(B*)pC 等價於 static_cast + reinterpret_cast 混合,**極度危險**,嚴禁用於多態類層次。int main() {
C* pC = new C();
B* pB_d = dynamic_cast<B*>(pC);
A* pA_d = dynamic_cast<A*>(pC);
B* pB_s = static_cast<B*>(pC);
A* pA_s = static_cast<A*>(pC);
printf("pC=%p pB_d=%p pA_d=%p pB_s=%p pA_s=%p\n", pC, pB_d, pA_d, pB_s, pA_s);
// 輸出示例:pC=0x55a1 pB_d=0x55b1 pA_d=0x55a1 pB_s=0x55b1 pA_s=0x55a1
// B 子對象偏移 sizeof(A),A 子對象地址不變
delete pC;
return 0;
}
多重繼承指針轉換的本質是內存地址偏移計算。編譯器根據類定義順序、虛表結構、虛基類表在編譯期(static_cast)或運行期(dynamic_cast)完成調整。深入理解這一機制,不僅能避免 UB,更是掌握 C++ 對象模型、編寫高性能多態代碼的基石。