Virtual Table
多重继承下的多态实现
这里详细分析
base2 *b2 = new derive();
b2->f2();
是如何实现从基类到派生类f2()函数的调用。
b2指针已指向了derive对象的base2部分,然后b2->f2()从base2-vtable对应的虚函数表的第一项,找到了non-virtual thunk to derive::f2(),然后调用。
咦,这里不应该是derive::f2()吗,那个non-virtual thunk to derive::f2()是什么鬼?
答案是和this指针强相关。
derive::f2()
函数的this指针肯定是derive*
类型的,而这里的b2是base2*
类型,不能直接调用。
non-virtual thunk to derive::f2()
代码其实是两行汇编,它完成出b2指针从base*
类型转换成derive*类型的功能,也即地址减去8。
安全性问题
通过父类型的指针访问子类自己的虚函数
我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:
Base1 *b1 = new Derive(); b1->f1(); //编译出错
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。
但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。
访问non-public的虚函数
另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。
class Base {
private:
virtual void f() { cout << "Base::f" << endl; }
};
class Derive : public Base{};
typedef void(*Fun)(void);
void main() {
Derive d;
Fun pFun = (Fun)*((int*)*(int*)(&d)+0);
pFun();
}
Reference
Last updated
Was this helpful?