2014-04-30 38 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的末尾,而不是放在一个单独的表中。这是有道理的:扩展一个现有的表比添加一个额外的更有效,这将扩大每个对象与每个继承级别的额外指针。

所有这些都是依赖于实现的。该语言没有指定如何实施虚拟调度,只是如何正常使用它应该工作。