虚函数表

在hairetz的一篇转的博文中(下面的链接)提到虚函数表解析,写的非常不过,不过那篇没有讨论虚继承情况,我自己试了虚继承情况。

http://blog.csdn.net/hairetz/archive/2009/04/29/4137000.aspx

 

下面是在VS08环境中测试的。

 

class Base
{
public:
 Base():a(10){}
 virtual void f(){cout<<"Base::f"<<endl;}
 virtual void g(){cout<<"Base::g"<<endl;};
 int a;
};

class Derived:virtual public Base
{
public:
 virtual void f1() { cout << "Derived::f1" << endl; }

 virtual void g1() { cout << "Derived::g1" << endl; }

 virtual void f() { cout << "Derived::f" << endl; }
};

int main()
{
 typedef void (*Fun)();
 Derived d;
 Fun pFun;
 pFun=(Fun)*((int*)*((int*)(&d)+0)+0);
 pFun();
 pFun=(Fun)*((int*)*((int*)(&d)+0)+1);
 pFun();
 cout<<*((int*)*((int*)(&d)+0)+2)<<endl;

 pFun=(Fun)*((int*)*((int*)(&d)+2)+0);
 pFun();
 pFun=(Fun)*((int*)*((int*)(&d)+2)+1);
 pFun();
 cout<<*((int*)*((int*)(&d)+2)+2)<<endl;
 
 cout<<sizeof(Derived)<<endl;
 cout<<*((int*)(&d)+3)<<endl;//虚基类中的a成员变量

 return 0;
}

 

输出

Derived::f1
Derived::g1
0
Derived::f
Base::g
0
16
10

 

从输出的大小看继承类大小16,包括基类的8字节(a和vptr),继承类自己的vptr,还有指向基类的对象的指针。所以是16字节。

注意这里继承类有自己的虚函数,不同于普通继承,普通的public继承会把子类的vtable中的函数指针合并到父类的vtable中,虚继承中会子类有自己的vtable和vptr。另外,子类会有指向父类对象subobject的指针,这个是我在 深入探索c++对象模型 中看到的。不同的编译器对这个实现也不一样,g++下运行结果也不相同。所以这个不用再深入了。

 

下面分析上面运行结果。

 

(int*)(&d)+0是d的第一个字节,(int*)*((int*)(&d)+0)+0 是虚函数表的第一项,后面+1表示虚函数表的第二项,由于子类只有两个虚函数,所以vtable中只有两个,后面是0表示结束的。从输出看输出的是子类的函数,所以在虚继承中子类在前,这个普通继承是不一样的。

int*)(&d)+2是d的第3个字节,(int*)*((int*)(&d)+2)+0是vtable的第一项,后面+1是第二项,从输出看这个确实是基类的vtable,而且由于子类覆盖了f函数,所以输出被子类的替换了,同样vtable最后一项是0表结束。

 

如果理解基本的虚函数表,这里应该不难理解。这里问题主要是虚继承时子类对象的结构,从 深入探索c++对象模型 中对照,VS基本还是和书上讲的实现方法是一样的,gcc不一样。至于这里第二个指针指向基类的指针 ,我试着验证地址老是不对,只好放弃。

 

下面是gcc下的结果

#include <iostream>
using namespace std;

class Base
{
        virtual void f(){cout<<"Base::f"<<endl;}
        virtual void g(){cout<<"Base::g"<<endl;};
  int a;
};

class Derived:virtual public Base
{
public:

        virtual void f1() { cout << "Derived::f1" << endl; }

        virtual void g1() { cout << "Derived::g1" << endl; }

        virtual void f() { cout << "Derived::f" << endl; }
};

int main()
{
  Derived d;
  typedef void (*Fun)();
  Fun pFun;
  cout<<sizeof(Derived)<<endl;

  pFun=(Fun)*((int*)*((int*)(&d))+1);
  pFun();

  pFun=(Fun)*((int*)*((int*)(&d)+1)+1);
  pFun();
  return 0;
}

结果
12
Derived::g1
Base::g
同样gcc下继承类在前面,基类在后面,但只有3*4的大小,看起来没有指向基类的指针。如果基类没有成员a的话,继承类只是4字节,子类和基类的vtable会合并到一起。

 

关于菱形结构的虚继承也不研究了,VC和g++的实现不一样,我觉得就没必要再深入了。

这些在求对象大小sizeof的时候也会要判断各种情况,由此可见不同编译器还是不一样。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值