各個編譯器對虛函數的實現有各自區別,但原理大體相同。本文基於VS2008探索虛函數ios
1 #pragma once 2 #include <iostream> 3 using namespace std; 4 class C1 5 { 6 public: 7 C1() 8 { 9 mem1 = 1; 10 mem2 = 2; 11 } 12 virtual void f1() 13 { 14 cout << "this is C1 f1" << endl; 15 } 16 virtual void f2() 17 { 18 cout << "this is C1 f2" << endl; 19 } 20 virtual ~C1() 21 { 22 cout << "this is C1 deconstruction" << endl; 23 } 24 int mem1; 25 int mem2; 26 }; 27 28 typedef void (*Fun)(void); 29 30 //調用虛函數 31 template<class F> 32 void CallVirtualFun(const int* p_virTableAddrPtr, const int index) 33 { 34 int funAddr = 0; //定義函數地址數值,用int取函數地址 35 int* p_funAddr = NULL; //真實虛函數地址 36 F fun = NULL; //函數指針 37 38 memcpy(&funAddr ,p_virTableAddrPtr + index, 4); //從虛函數表中取函數地址,結果保存在funAddr 39 p_funAddr = (int*)funAddr; //將函數地址數值轉換爲函數地址 40 fun = (F)p_funAddr; //轉函數指針 41 fun(); //調用函數 42 } 43 44 void VirtualFunByOffset() 45 { 46 C1 mC1; 47 //取出類地址 48 int* p_classAddr = (int*)(&mC1); 49 //取出虛表的地址,類的前4個字節是虛表指針,首先取出虛表地址 50 int virTableAddrInt = *p_classAddr; 51 //根據虛表的地址訪問虛表,虛表每一項都是函數指針,佔有4個字節 52 int* p_virTableAddrPtr = (int*)virTableAddrInt; 53 54 //將虛函數地址轉換爲函數指針 55 CallVirtualFun<Fun>(p_virTableAddrPtr, 0); //調用虛函數表的第一個虛函數 56 CallVirtualFun<Fun>(p_virTableAddrPtr, 1); //調用虛函數表的第二個虛函數 57 };
聲明虛虛構,看出虛析構函數在虛函數表中第一個位置函數
將虛析構挪到末尾處聲明,看出虛析構在虛函數表末尾位置測試
以C1爲例,構建對象模型,以下圖所示:this
本冊測試結構爲spa
首先建立一個類對象,經過類起始地址獲取獲得類的前4個字節,即虛表地址。3d
拿到虛表地址後,查看虛表。函數指針每4個字節爲1個單位,下面的虛表地址下共有3個虛函數,虛表以0結束。指針
取得虛表的第一個函數,這裏是C1::f1,地址是00 0a 10 c3,訪問該地址。並將該地址轉換爲函數指針:調試
調用後進入函數體code