各个编译器对虚函数的实现有各自区别,但原理大体相同。本文基于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