C++中的 虚表(vtable)和虚指针(vptr)

1、每个c++类都有一个vtable,每个类对象都有个vtable的vptr;
2、在vc中,类的继承是在基类后面追加数据的形式进行继承的。


测试代码如下(在32位系统下的vs2008环境中测试的):


class Test
{
public:
Test(int a) {data = a;}
virtual ~Test() {cout<<"Test deconstruct"<<endl;}//基类中的虚析构函数
virtual void fun11() {cout<<"Test virtual fun11"<<endl;}//基类中的虚函数fun11
virtual void fun12() {cout<<"Test virtual fun12"<<endl;}//基类中的虚函数fun12
int data;
};


class Test1:public Test
{
   public:
Test1(int d1, int d2):Test(d2) {data1 = d1;data2 = d2;}
     int data1;
     int data2;
virtual ~Test1() {cout<<"test1 deconstruct"<<endl;}//派生类中的虚析构函数
virtual void fun1() {cout<<"test1 virtual fun1"<<endl;}//派生类中的虚函数fun1,不是实现基类中的fun11的多态
virtual void fun2() {cout<<"test1 virtual fun2"<<endl;}//派生类中的虚函数fun2,不是实现基类中的fun12的多态
};
 
typedef void (*Fun)(void);//指向函数的指针


int main()
{
cout<<"test in 32bit system"<<endl;


Test1 obj(1,2);//定义对象obj
   cout << "obj's Size = " << sizeof(obj) << endl;
   cout << "obj 's Address = " << &obj << endl;
   cout<<"second Test1 object's address = "<<&obj + 1<<endl;//为了测试(int*)(&obj+1)和((int*)&obj+1)的区别
   cout<<"third Test1 object's address = "<<&obj + 2<<endl;
   cout<<"base data address and data:        "<<(int*)&obj + 1<<"\t"<<*((int*)&obj + 1)<<endl;
   cout<<"derivate data1 address and data1:"<<(int*)&obj + 2<<"\t"<<*((int*)&obj + 2)<<endl;
   cout<<"derivate data2 address and data2:"<<(int*)&obj + 3<<"\t"<<*((int*)&obj + 3)<<endl;
   
   //获得虚表指针,显示虚表中的内容
   cout<<"vtable address = "<<(int*)&obj<<"\t"<<"value = "<<*((int*)&obj + 0)<<endl;
   cout<<"vtable value0 = "<<*((int*)*(int*)((int*)&obj+0)+0)<<endl;
   cout<<"vtable value1 = "<<*((int*)*(int*)((int*)&obj+0)+1)<<endl;
   cout<<"vtable value2 = "<<*((int*)*(int*)((int*)&obj+0)+2)<<endl;
   cout<<"vtable value3 = "<<*((int*)*(int*)((int*)&obj+0)+3)<<endl;
   cout<<"vtable value4 = "<<*((int*)*(int*)((int*)&obj+0)+4)<<endl;
   cout<<"vtable value5 = "<<*((int*)*(int*)((int*)&obj+0)+5)<<endl;


   Fun pFun = NULL;
   pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
   pFun();


   pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2);
   pFun();


   pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
   pFun();


   pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+4);
   pFun();
   return 0;
}


输出结果:


此时的派生类虚表结构为:

virtual ~Test()
virtual void fun11()
virtual void fun12()
virtual void fun1()
virtual void fun2()
 


将上述程序中标红的程序分别改为下面相应的程序:
~Test() {cout<<"Test deconstruct"<<endl;}
virtual ~Test1() {cout<<"test1 deconstruct"<<endl;}//派生类中的虚析构函数
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+4);

结果为:

此时的派生类虚表结构为:

virtual void fun11()
virtual void fun12()
virtual ~Test1()
virtual void fun1()
virtual void fun2()
 


将上述程序中标红的程序再分别改为下面相应的程序:
virtual ~Test() {cout<<"Test deconstruct"<<endl;}
//virtual ~Test1() {cout<<"test1 deconstruct"<<endl;}//派生类中的虚析构函数
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
 pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+4);

结果为:


此时的派生类虚表结构为:
此时的派生类虚表结构为:

virtual ~Test()
virtual void fun11()
virtual void fun12()
virtual void fun1()
virtual void fun2()
 


分析:

1、这个对象的大小为16byte,包括最前面的一个int*型的虚表指针和3个int数据。
2、(int*)(&obj+1)和((int*)&obj+1)的区别是:前面表示的是地址按对象大小增加,后面表示的是一个对象地址按4个字节增加。
3、虚表中存放的是所有虚函数的首地址,存放顺序和(继承顺序、函数的声明顺序)有关。
4、当派生类中重载了基类中的函数时,(包括析构函数),这时候虚表中就只会有基类中的虚函数的地址,但是当调用派生类中重载的虚函数时,实现的是重载的功能。
5、析构函数调用顺序,先是派生类再是基类。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章