指針偏移現象
class Base1 { virtual void f1(); };
class Base2 { virtual void f2(); };
class Derive : public Base1, public Base2 { ... };
Derive d;
Base1* p1 = &d; // 指向 Derive 起始地址 (Base1 子對象在最前)
Base2* p2 = &d; // 指向 Base2 子對象地址 (偏移 sizeof(Base1))
// p1 == &d 為 true,p2 == &d 為 false
// 但 p1 == p2 在編譯器隱式轉換下為 true(比較邏輯地址)
內存佈局
Derive 對象:
[ Base1 部分 ] [ Base2 部分 ] [ Derive 自身部分 ]
↑ p1, &d ↑ p2
強制轉換陷阱
Base1* p1 = &d;
Base2* p2 = (Base2*)p1; // 錯誤!強制轉換不調整偏移,p2 指向 Base1 內存
p2->f2(); // 未定義行為,可能調用 Base1::f1()(虛表錯位)
// 正確:dynamic_cast
Base2* p2 = dynamic_cast(p1); // 正確調整偏移
反向轉換風險
Base2* p2 = &d;
Base1* p1 = (Base1*)p2; // 可能崩潰!Base2 不在對象起始位置
核心規則
- 多重繼承下,基類指針轉換必須用
dynamic_cast(或 static_cast 但需確知佈局)
- C 風格強轉
(Type*) 不調整指針值,極度危險
dynamic_cast 需虛函數支援,運行時檢查並調整偏移
- 避免多重繼承,或使用組合/介面隔離
驗證代碼
int main() {
Derive d;
cout << "&d=" << &d << "\n";
Base1* p1 = &d;
cout << "p1=" << p1 << "\n";
Base2* p2 = dynamic_cast(p1);
cout << "p2=" << p2 << "\n";
p2->f2(); // 正確輸出 Derive::f2
return 0;
}