多重继承时的虚函数表
分析以下一段代码(vs2010编译)
1 #include<iostream> 2 using namespace std; 3 class ba 4 { 5 public: 6 double dd; 7 }; 8 class bas 9 { 10 public: 11 double d; 12 }; 13 14 class base1 15 { 16 char u; 17 public: 18 virtual void h(){cout<<"base1"<<endl;} 19 virtual void f(){cout<<"base1"<<endl;} 20 }; 21 class base2 22 { 23 public: 24 virtual void k(){cout<<"base2"<<endl;} 25 virtual void g(){cout<<"base2"<<endl;} 26 virtual void f(){cout<<"base2"<<endl;} 27 28 }; 29 30 class base3 31 { 32 public: 33 virtual void e(){cout<<"base3"<<endl;} 34 virtual void f(){cout<<"base3"<<endl;} 35 virtual void g(){cout<<"base3"<<endl;} 36 }; 37 class deri:public ba,public base1,public base2,public bas,public base3 38 { 39 char ch; 40 public: 41 void g(){} 42 virtual void ff(){cout<<"deri ff"<<endl;} 43 void f(){cout<<"deri f"<<endl;} 44 void e(){} 45 }; 46 int main() 47 { 48 deri dd; 49 base1* pb1=ⅆ 50 base2* pb2=ⅆ 51 printf("%x\n",*((int*)pb1)); 52 cout<<pb1<<endl; 53 cout<<pb2<<endl; 54 return 0; 55 }
deri类的内存分布是这样的:
也就是说,含有虚函数的父类排列在没有虚函数的父类之前。在这两种类的内部,父类的分布是按继承时的声明次序排列的,每个父类的成分都非常完整。这个deri类有三个虚函数表指针,同时注意虚函数指针所带来的内存对齐。
对于每一张虚函数表,虚函数在表中的排列次序是按照类中的声明次序来的。deri类自己新增的虚函数会添加到第一张虚函数表的末尾。
对于至少两个父类均有的虚函数(f(),g()),第一个拥有该虚函数的父类的虚表中填入了函数的地址(&deri::f,&deri::g),之后的表都只是记录一个偏移值,即自身指针减去这个偏移值之后(this-=8 this-=12 this-=4),就可以得到第一个拥有该虚函数的父类的起始地址,然后可以调用相应的虚函数。
最后四行是指明了deri类中虚函数的地址分别所在的表的偏移。