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