使用vptr直接调用虚函数时了解输出

时间:2014-04-30 12:03:31

标签: c++ inheritance virtual vtable vptr

我正在浏览我从某处获得的代码,以了解vptr和vtable的工作原理。以下是输出

的代码
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访问的虚函数的地址,最终指向派生类的vtable。

2 个答案:

答案 0 :(得分:0)

面向对象编程的整个想法是遵循抽象。当您具有用于动态绑定的虚函数时,通过调用基类指针的虚函数来通过抽象使用它。根据它在类层次结构中动态指向的位置,它将动态绑定。为什么要更深入地打破抽象并使用vtbl等(根据某些指导原则,编译器到编译器的实现各不相同)。所以建议是要善用动态绑定。

答案 1 :(得分:0)

看起来Derive中的新虚函数被添加到第一个基类的vtable的末尾,而不是放在一个单独的表中。这是有道理的:扩展现有表比添加额外的表更有效,这会使每个对象膨胀,并为每个继承级别添加额外的指针。

当然,所有这些都取决于实现。该语言没有说明应该如何实现虚拟调度,只指定正常使用时它应该如何工作。