2014-04-30 82 views
0

我正在瀏覽從某處獲得的代碼,以瞭解vptr和vtable的工作原理。以下是帶輸出的代碼當使用vptr直接調用虛函數時理解輸出

class Base1 
{ 
virtual void fun1() { cout<< "Base1::fun1()" << endl; } 
virtual void func1() { cout<< "Base1::func1()" << endl; } 
}; 
class Base2 { 
virtual void fun1() { cout<< "Base2::fun1()" << endl; } 
virtual void func1() { cout<< "Base2::func1()" << endl; } 
}; 
class Base3 { 
virtual void fun1() { cout<< "Base3::fun1()" << endl; } 
virtual void func1() { cout<< "Base3::func1()" << endl; } 
}; 

class Derive : public Base1, public Base2, public Base3 
{ 
public: 
virtual void Fn() 
{ 
cout<< "Derive::Fn" << endl; 
} 
virtual void Fnc() 
{ 
cout<< "Derive::Fnc" << endl; 
} 
}; 
typedef void(*Fun)(void); 

int main() 
{ 
Derive obj; 
Fun pFun = NULL; 
// calling 1st virtual function of Base1 
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0); 
pFun(); 
// calling 2nd virtual function of Base1 
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1); 
pFun(); 
// calling 1st virtual function of Base2 
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0); 
pFun(); 
// calling 2nd virtual function of Base2 
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1); 
pFun(); 
// calling 1st virtual function of Base3 
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0); 
pFun(); 
// calling 2nd virtual function of Base3 
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1); 
pFun(); 
// calling 1st virtual function of Derive 
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2); 
pFun(); 

// calling 2nd virtual function of Derive 
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3); 
pFun(); 
return 0; 
} 

OUTPUT: 
Base1::fun 
Base1::func 
Base2::fun 
Base2::func 
Base3::fun 
Base3::func 
Derive::Fn 
Derive::Fnc 

它看起來不錯,但派生類的虛擬函數被調用的方式不被理解。它不應該是這樣的:

// calling 1st virtual function of Derive 
pFun = (Fun)*((int*)*(int*)((int*)&obj+3)+0); 
pFun(); 

// calling 2nd virtual function of Derive 
pFun = (Fun)*((int*)*(int*)((int*)&obj+3)+1); 

這意味着是虛函數的地址,利用其最終指向的派生類的虛函數表的派生類的vptr訪問。

+0

虛擬調用機制是實現定義的。 –

+2

忽視未定義的行爲,這取決於編譯器。閱讀你的編譯器的文檔和/或來源,找出它應該是什麼。 – molbdnilo

回答

0

面向對象編程的整個概念是遵循抽象。當你有用於動態綁定的虛擬函數時,通過調用基類指針的虛函數在抽象中使用它。根據它在類層次結構中動態指向的位置,它將動態綁定。爲什麼你應該更深入地打破抽象並使用vtbl等(根據一些指南,實現因編譯器而異)。所以建議使用良好的動態綁定。

0

它看起來像Derive中的新虛函數被添加到第一個基類的vtable的末尾,而不是放在一個單獨的表中。這是有道理的:擴展一個現有的表比添加一個額外的更有效,這將擴大每個對象與每個繼承級別的額外指針。

所有這些都是依賴於實現的。該語言沒有指定如何實施虛擬調度,只是如何正常使用它應該工作。