淺談多型中的虛擬函式和虛表
阿新 • • 發佈:2019-02-09
需要實現多型必不可少的就是虛擬函式,類的成員函式前加virtual關鍵字,這個成員函式就是虛擬函式;例如:
class T
{
public:
virtual void fun()
{
cout<<"fun()"<<endl;
}
int _t;
};
在不加virtual的情況下:sizeof(T)的大小為4;加了vitual變成虛擬函式之後:sizeof(T)的大小為8;
這是為什麼呢??
存在虛擬函式的類的物件模型為:
由此圖可以看出T的物件中不僅有_t而且還有一個指標,這個指標指向的是一個存放虛擬函式地址的表,也就是虛表。所以在類成員函式前加了virtual之後的大小為8;
根據下圖就可以看出虛表中是怎麼去存放虛擬函式的地址:
在單繼承下的虛擬函式表又是什麼樣??
上述程式碼中,類Z單繼承T,重寫了T類中的fun1,fun2並沒有重寫,在Z類本身中還加了自己的虛擬函式fun3;class T{ public: virtual void fun1() { cout << "T::fun1()" << endl; } virtual void fun2() { cout << "T::fun1()" << endl; } int _t; }; class Z : public T{ public: virtual void fun1() { cout << "Z::fun1" << endl; } virtual void fun3() { cout << "Z::fun3" << endl; } int _z; };
Z的物件模型為:
vs2013監視視窗:
由上圖監視視窗可以看出在__vfPtr所指的虛表中只有Z::fun1和T::fun2這兩個虛擬函式的地址,fun3並沒有存在於虛表中,這是因為vs2013下編譯器的一個bug,不過我們
換一種方式(列印虛表)來看虛表:
class T{ public: virtual void fun1() { cout << "T::fun1()" << endl; } virtual void fun2() { cout << "T::fun1()" << endl; } int _t; }; class Z : public T{ public: virtual void fun1() { cout << "Z::fun1()" << endl; } virtual void fun3() { cout << "Z::fun3()" << endl; } int _z; }; typedef void (*V_F) (); void PrintVtable(int vptr) { int * ptr = (int *)vptr; printf("虛表:0x%p\n",ptr); for (int i = 0; ptr[i] != 0; i++) //通常虛表以0為結束標誌; { V_F f = (V_F)ptr[i]; f(); } } void test() { Z z; PrintVtable(*(int*)(&z)); }
執行截圖:
多繼承的虛擬函式表:
class T{
public:
virtual void fun1()
{
cout << "T::fun1()" << endl;
}
virtual void fun2()
{
cout << "T::fun2()" << endl;
}
int _t;
};
class Z {
public:
virtual void fun1()
{
cout << "Z::fun1()" << endl;
}
virtual void fun2()
{
cout << "Z::fun2()" << endl;
}
int _z;
};
class P : public Z,public T{
public:
virtual void fun1()
{
cout << "P::fun1()" << endl;
}
virtual void fun3()
{
cout << "P::fun3()" << endl;
}
};
typedef void (*V_F) ();
void PrintVtable(int vptr)
{
int * ptr = (int *)vptr;
printf("虛表:0x%p\n",ptr);
for (int i = 0; ptr[i] != 0; i++) //通常虛表以0為結束標誌;
{
V_F f = (V_F)ptr[i];
f();
}
}
void test()
{
P p;
PrintVtable(*(int *)(&p));
}
執行截圖:
可以看出在虛擬函式表中沒有出現虛擬函式T::fun2();這是因為派生類中的虛擬函式會放在第一個繼承的虛表中;