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、析構函數調用順序,先是派生類再是基類。

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