本文的例子来自<<Inside The C++ Object Model>> P159 ,转发请注明出处,谢谢。
1.先给出测试代码和测试结果
#include<iostream>
using namespace std;
class Base1
{
public:
virtual ~Base1() {};
virtual void speakClearly() {cout<<"Base1::speakClearly()"<<endl;}
virtual Base1 *clone() const {cout<<"Base1::clone()"<<endl; return new Base1;}
protected:
float data_Base1;
};
class Base2
{
public:
virtual ~Base2() {};
virtual void mumble() {cout<<"Base2::mumble()"<<endl;}
virtual Base2 *clone() const {cout<<"Base2::clone()"<<endl; return new Base2;}
protected:
float data_Base2;
};
class Derived : public Base1,public Base2
{
public:
virtual ~Derived() {cout<<"Derived::~Derived()"<<endl;}
virtual Derived *clone() const {cout<<"Derived::clone()"<<endl; return new Derived;}
protected:
float data_Derived;
};
int main()
{
cout<<sizeof(Base1)<<endl;
typedef void (*Fun) ();
Fun pFun = NULL;
//******************************************************//
// vitrual table[0]
//******************************************************//
cout<<"################ test virtual table[0] ################"<<endl<<endl;
Derived d;
cout<<"&d = "<<&d<<endl;
int *vptr1 = (int*)*((int*)&d+0); //vptr1为virutal table[0]的地址
cout<<"vptr1 = "<<vptr1<<endl;
int *pf1 = (int*)*((int*)*((int*)&d+0)+0); //pf1为virtual table[0]里的第一个虚拟函数Derived::~Derived()的地址
//int *pf1 = (int*)*((int*)vptr1[0]); //与上面等价
cout<<"&vptr1[0] = "<<&vptr1[0]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pf1 = (int*)*((int*)*((int*)&d+0)+1) ;//pf1为virtual table[0]里的第二个虚拟函数Base1::speakClearly()的地址
cout<<"&vptr1[1] = "<<&vptr1[1]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pFun = (Fun)pf1;
pFun();
pf1 = (int*)*((int*)*((int*)&d+0)+2) ;//pf1为virtual table[0]里的第三个虚拟函数Derived::clone()的地址
cout<<"&vptr1[2] = "<<&vptr1[2]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pFun = (Fun)pf1;
pFun();
pf1 = (int*)*((int*)*((int*)&d+0)+3) ;
cout<<"&vptr1[3] = "<<&vptr1[3]<<endl;
cout<<"pf1 = "<<pf1<<endl;
if(pf1 == NULL)
cout<<"NUll"<<endl; //结果表明,virtual table[0][3]为NULL
//******************************************************//
// vitrual table[1]
//******************************************************//
cout<<endl<<endl<<"################ test virtual table[1] ################"<<endl<<endl;
int sz = sizeof(Base1)/4;
int *vptr2 = (int*)*((int*)&d+sz); //vptr2为virutal table[1]的地址
cout<<"vptr2 = "<<vptr2<<endl;
pf1 = (int*)*((int*)*((int*)&d+sz)+0); //pf1为virtual table[1]里的第一个虚拟函数Derived::~Derived()的地址
cout<<"&vptr2[0] = "<<&vptr2[0]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pf1 = (int*)*((int*)*((int*)&d+sz)+1); //pf1为virtual table[1]里的第二个虚拟函数Based::mumble()的地址
cout<<"&vptr2[1] = "<<&vptr2[1]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pFun = (Fun)pf1;
pFun();
pf1 = (int*)*((int*)*((int*)&d+sz)+2); //pf1为virtual table[1]里的第三个虚拟函数Derived::clone()的地址
cout<<"&vptr2[2] = "<<&vptr2[2]<<endl;
cout<<"pf1 = "<<pf1<<endl;
pFun = (Fun)pf1;
pFun();
pf1 = (int*)*((int*)*((int*)&d+sz)+3) ;
cout<<"&vptr2[3] = "<<&vptr2[3]<<endl; //??????
cout<<"pf1 = "<<pf1<<endl;
if(pf1 == NULL)
cout<<"NUll"<<endl;
pFun = (Fun)pf1; // 虚函数表里问什么有两个地址指向Derived::clone()
pFun();
pf1 = (int*)*((int*)*((int*)&d+sz)+4) ;
cout<<"&vptr2[4] = "<<&vptr2[4]<<endl;
cout<<"pf1 = "<<pf1<<endl;
if(pf1 == NULL)
cout<<"NUll"<<endl; //结果表明,virtual table[0][3]为NULL
return 0;
}
用VS2010测试的结果:
结果分析:
2.总结
1).派生类的虚函数表数目是它所有基类的虚函数数目之和,基类的虚函数表被复制到派生类的对应的虚函数表中。
2).派生类中重写基类的虚拟函数时,该被重写的函数在派生类的虚函数列表中得到更新,派生类的虚析构函数覆盖基类的虚析构函数。
3).派生类中新增加的虚函数被添加到与第一个基类相对应的虚函数表中。
4).测试表明:在VS2010的C++编译器中,虚函数表不总是以NULL结束。
5).virtual table[1]中的clone分别为:Base2* Derived::clone 和 Derived* Derived::clone 。这里为什么会比table[0]多一个两个Base2* Derived::clone呢?因为:如果将一个Derived对象地址指定给一个Base1指针或者Derived指针是,虚拟机制使用的是virtual table[0] ;如果将一个Derived对象地址指定给一个Base2指针时,虚拟机制使用的是virtual table[1]。 (<<C++对象模型>> P164)
通过命令行 /d1reportSingleClassLayoutDerived 可以看到Derived的内存布局:
1> class Derived size(20):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | data_Base1
1> | +---
1> | +--- (base class Base2)
1> 8 | | {vfptr}
1> 12 | | data_Base2
1> | +---
1> 16 | data_Derived
1> +---
1>
1> Derived::$vftable@Base1@:
1> | &Derived_meta
1> | 0
1> 0 | &Derived::{dtor}
1> 1 | &Base1::speakClearly
1> 2 | &Derived::clone
1>
1> Derived::$vftable@Base2@:
1> | -8
1> 0 | &thunk: this-=8; goto Derived::{dtor}
1> 1 | &Base2::mumble
1> 2 | &thunk: this-=8; goto Base2* Derived::clone
1> 3 | &thunk: this-=8; goto Derived* Derived::clone