虚函数探秘
C++的多態(tài)分為動態(tài)多態(tài)和靜態(tài)多態(tài),其中靜態(tài)多態(tài)主要靠重載和模板來實(shí)現(xiàn),而動態(tài)多態(tài)則主要靠繼承來實(shí)現(xiàn)了。
那么靜態(tài)和動態(tài),怎么算靜,怎么算動呢?靜態(tài)多指編譯期能決定的事情,而動態(tài)多指運(yùn)行時(shí)才決定的事情。例如重載,在編譯期生成符號的時(shí)候就已經(jīng)確定不同的函數(shù)了,而繼承的重寫(override)則是在運(yùn)行到具體的代碼位置確認(rèn)指針內(nèi)部的虛函數(shù)表指向的函數(shù)地址時(shí)才知道執(zhí)行哪個(gè)函數(shù),被稱為動態(tài)多態(tài)。
虛函數(shù)表指針
虛函數(shù)表指針指向虛函數(shù)表,虛函數(shù)表每個(gè)類一個(gè),如果是單繼承的話子類和父類的虛函數(shù)表會合體,下面是一個(gè)簡單的例子
class Father { public:virtual void fatherfunction() {cout << "fatherfunction" << endl;} };class Child : public Father { public:virtual void childfunction() {cout << "childfunction" << endl;} };Child c; Father f;用過調(diào)試器查看變量內(nèi)部可以看到Child類的對象c中有父類的虛函數(shù)表指針,但是自己的呢?單純一個(gè)父類對象也有自己的虛函數(shù)表指針,為什么Child類的對象自己的虛表指針不見了呢?
那么一個(gè)猜測就是子類的虛函數(shù)表和父類的虛函數(shù)表合體了,這個(gè)猜測到底正不正確呢?得想個(gè)辦法驗(yàn)證一下!
一般虛表指針存放在對象地址空間的頭4位,這里先取得上圖中看到的Father的vptr指向的虛表,虛表地址的獲取需要獲取虛表指針中存放的值。
cout << hex << "virtual table address: " << *(int *)(&c) << endl;然后是獲得fatherfunction函數(shù)指針的地址
cout << hex << "fatherfunction address: " << (int *)*(int *)(&c) << endl; typedef void(*FUN)();Child c; cout << hex << "virtual table address: " << *(int *)(&c) << endl; cout << hex << "fatherfunction address: " << (int *)*(int *)(&c) << endl;FUN father_func = (FUN)*((int *)*(int *)(&c));FUN child_func = (FUN)*((int *)*(int *)(&c) + 1);father_func(); child_func();得到的結(jié)果如下圖所示
虛函數(shù)表中存放的緊接著father_func函數(shù)地址的下一個(gè)就是child_func的地址,因此是合并了。
PS:簡單解釋一下取函數(shù)指針的代碼
續(xù)表為手誤,是虛表。圖不好改就不改了
靜態(tài)函數(shù)不能是虛函數(shù)
靜態(tài)成員不屬于任何對象,所以就算加上了virtual也沒有意義
靜態(tài)函數(shù)沒有this指針,虛函數(shù)表存在于對象的地址空間,使用對象的this指針訪問,但是靜態(tài)函數(shù)沒有this指針故也無法訪問虛函數(shù)表。
靜態(tài)函數(shù)不能是const也不能是volatile
const成員函數(shù)要求使用者為const類對象,再換一個(gè)說法就是要求this指針為const,但是靜態(tài)函數(shù)沒有this指針,故使用const關(guān)鍵字修飾靜態(tài)函數(shù)毫無意義
PS:volatile是一個(gè)關(guān)鍵字用來表示當(dāng)前語句不會被編譯器優(yōu)化,且要求每次直接讀值
多繼承的時(shí)候虛表指針表現(xiàn)
多繼承時(shí),對象內(nèi)部會有多個(gè)虛表指針指向多個(gè)虛函數(shù)表,如果自己也能繼續(xù)繼承,那么自己的續(xù)表會和第一個(gè)虛表合體,虛表合體這個(gè)在上面已經(jīng)描述過了,多個(gè)多繼承的話也會合并,是會和內(nèi)存空間的第一個(gè)虛表合并。
class Father { public:virtual void fatherfunction() {cout << "fatherfunction" << endl;} };class Mother { public:virtual void motherfunction() {cout << "motherfunction" << endl;} };class Child : public Father, public Mother { public:virtual void childfunction() {cout << "childfunction" << endl;} };把類的繼承關(guān)系改成這樣,讓Child類繼承自Father和Mother,此時(shí)Child的對象c中有幾個(gè)虛表指針?
答案是兩個(gè)分別是兩個(gè)類的虛表指針,此時(shí)為了判斷合體,還是用上面提到的方法,也能成功,發(fā)現(xiàn)子類的虛表是存在頭4個(gè)字節(jié)的虛表指針?biāo)赶虻奶摫韮?nèi)的。
而這個(gè)虛表指針的先后順序和聲明繼承的順序一致,public Mother如果在public Father之前的話就會變成如下圖所示
轉(zhuǎn)載于:https://www.cnblogs.com/lenomirei/p/11384480.html
總結(jié)
- 上一篇: linux路径表示方法?
- 下一篇: NOIP模拟测试29「爬山·学数数·七十