多重繼承與void*指標轉換問題的分析
阿新 • • 發佈:2019-01-27
C++支援多重繼承,然而多重繼承可能會導致一些奇怪的問題,我前段時間遇到一個指標轉換問題,非常典型。
先看一個簡單的測試程式碼:
強制轉換為void* 先static_cast再強制轉換為void* 先dynamic_cast再強制轉換為void*
#include <iostream> using namespace std; class IA { public: virtual ~IA(){} virtual void a() = 0; }; class IB { public: virtual ~IB(){} virtual void b() = 0; }; class CMulti : public IA, public IB { public: CMulti(){} ~CMulti(){} void a(){ cout << "C::a()" << endl; } void b(){ cout << "C::b()" << endl; } }; void testCastA(void *p) { cout << "cast from void*(" << p << ")to IA*: "; IA *a = (IA *)p; a->a(); } void testCastB(void *p) { cout << "cast from void*(" << p << ")to IB*: "; IB *b = (IB *)p; b->b(); } int _tmain(int argc, _TCHAR* argv[]) { CMulti * c = new CMulti; cout << "cast to void*, then to IA or IB:" << endl; testCastA((void*)c); testCastB((void*)c); cout << endl; cout << "static_cast to void*, then to IA or IB:" << endl; testCastA((void*)static_cast<IA*>(c)); testCastB((void*)static_cast<IB*>(c)); cout << endl; cout << "dynamic_cast to void*, then to IA or IB:" << endl; testCastA((void*)dynamic_cast<IA*>(c)); testCastB((void*)dynamic_cast<IB*>(c)); return 0; }
差異很明顯了,結論也很明瞭:多重繼承時,子類指標轉換為非第一繼承的父類指標時,會發生地址偏移(注意圖上標紅的部分)。這是因為每一個父類都會佔用 4 個位元組維護自己的虛擬函式表。所以,當 CMulti* 轉換為 IB* 時,指標加 4 ,因為 IA 是 CMulti 的第一父類, IB 是第二父類,依次類推……
如果我們在某些地方不得不使用 void* 來進行程式碼適配時,遇到多重繼承就要注意這一點,否則很可能你呼叫的是 b() 方法,實際執行的是 a() ,達導致異常甚至崩潰。