随笔 - 1, 文章 - 9, 评论 - 4, 引用 - 0
数据加载中……

vft、vptr、thunk

对于vft(vitrual function table或是vitrual method table)和vptr(vitrual pointer)做一些总结。

当类中有虚函数的时候才会建立vft,这个表里面按照顺序(从0开始)和类里面的虚函数做出对应。
如果一个类里面有虚函数(就是有vft),那它就有个vptr。vptr是一个存在在类中的一个隐含指针,这个指针指向的是vft这个表。
当调用类中的某个虚函数的时候,就通过这个指针去找vft里面对应的函数,然后拿来调用。

class Base
{
public:
    FunctionPointer 
*__vptr;
    
virtual void function1() {};
    
virtual void function2() {};
};

class D1: public Base
{
public:
    
virtual void function1() {};
};

class D2: public Base
{
public:
    
virtual void function2() {};
};

这里显示的把vptr显示出来,但实际上是看不见的。


注意d1和d2的vptr是继承自base的

int main()
{
    D1 cClass;
    Base *pClass = &
cClass;
    pClass->function1();
}

上面的代码之所以可以执行,是因为:pClass指针指向的仅仅是cClass中属于Base的部分(因为Base是D1的父类),因为vptr原本是在Base中的(虽然看不见),所以pClass是可以调用vptr的。再由于继承的关系,此时的vptr处于D1类中,它指向的是D1的vft,所以pClass->funtion1()这句可以执行成功。

说一个复杂的情况:

class A
{
public:
    
virtual void a();
};

class B : public A
{
public:
    
virtual void a();
    
virtual void c();
    
virtual void f();
};

class C : public A
{
public:
    
virtual void e();
};

class D : public B, public C
{
public:
    
void a();
    
void g();
}:

这里就有点问题,如果单纯的vft中按照函数顺序的话。在D这个类中,来自B和来自C这两个类中的B::c函数和C::e函数在他们的类中所在的位置从文本上看都是第二的位置(C类还有个A中继承的a函数)。那么对于D来说有同一个位置有两个函数,这时vft必然不能正常实现。在C++中其实D这个类有两个vft,一个基于B建立,一个基于C建立。当然这种情况下只有在多继承时才出现。

那么在实际运行时,首先要把vft的地址(也就是vptr的值)放到寄存器里面,然后要确定用的是哪个类的vft(对于D这个类来说),接着再索引这个函数表找到函数,最后才根据地址执行函数。

也就只对于多继承这种复杂的情况下,才有上面这么多步骤。对于单继承,只用找到表,找到索引,找到函数,调用即可。GCC的thunk在建立vft的时候就确定了到底这个函数是在哪个对象

load [object_reg+#VFToffset], table_reg
load [table_reg
+#deltaOffset], delta_reg
load [table_reg
+#numOffset], method_reg
add object_reg, delta_reg, object_reg
call method_reg

上面的汇编取自一篇论文,VFToffset就是vft的地址偏移,deltaOffset就是多继承产生的偏移(选哪个类,B还是C),numOffset就是函数偏移。省去的就是2、4这两条语句,thunk把这步做了。

关于thunk还是有点问题,不是非常明白。

posted on 2008-12-19 18:09 EiN 阅读(505) 评论(0)  编辑 收藏 引用 所属分类: C/C++ and ASM


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理