虚函数表剖析(借助gdb和vc工具)
2016-05-04 20:47
204 查看
问题
之前分析了c++中的类成员函数的重载、隐藏和覆盖之间的关系,我们从概念上有了清晰的区分,主要是分析覆盖与隐藏之间的关系,但是当子类的函数隐藏了基类的同名虚函数时,我并不清楚具体的实现机制是什么,我们都知道虚函数的调用发生在运行时期,每个类如果存在虚函数则就有相应的虚函数表,但在基类的虚函数被隐藏时,派生类的虚函数表中是否存在被隐藏的基类虚函数的指针,以及如何对虚函数表中的函数的查看,下面进行分析。class Base { public: virtual void func() { cout << "Base :" << endl; } virtual void func(int arg1) { cout << "Base :" << arg1 << endl; } void func(int arg1,int arg2) { cout << "Base :" << arg1 <<": "<< arg2 << endl; } }; class A: public Base { public: void func() { cout << "A :" << endl; } virtual void func1(){} }; int main() { Base base; A a; Base *test =&a; test->func();// 重载 //a.func(1);// 隐藏,编译错误 //a.func(1,2);//编译错误 test->func(1); // Base::func(int); }
上面的例子中,派生类A中的func()覆盖了Base类中的func()的同时,也隐藏了其它两个同名的函数。因此会出现编译错误,但是当将a转换为Base指针时可以调用隐藏的函数,因此不知道此时调用的虚函数是父类还是基类的虚函数表,如何查看此时派生类虚函数表中的函数指针。
分析
毫无疑问,我们知道Base中的虚函数表是一个函数指针数组,每一个虚函数指针占用虚函数表中的一个位置,具体32位机器是4个字节,64位8个字节。这是对象base中的虚函数分布,右边为Base类的虚函数表。每个有虚函数的类的对象为了访问虚函数方便,基本上都将虚函数表的指针放在对象的最开头,因此我们可以通过表达式(int*)*(int*)&a得到虚函数表的指针。本例中只是为了说明没有设置成员变量,因此对象的其它成员是空的。
typedef void (*func)(); cout << "虚函数表地址:" << ((int*)*(int*)(&base)) << endl; cout << "第一个函数表地址:" << (*((int*)*(int*)(&base))) << endl; func pFunc = (func)(*((int*)*(int*)(&base))); pFunc();
获得了函数指针,按照上面的方式,我们可以直接对类成员函数进行访问(基于c++对于指针的强大的处理能力,当然这是违反原则的),可以说c++是集于天使和魔鬼于一生。。但是当试着访问带参数的func(int)在函数返回时会出现ESP错误,不知道什么缘故,反正只是为了探明真相,实际中并不会这样应用的。
但是派生类中的虚函数是怎么样的呢,下面利用vc和gdb进行查看
查看
vc查看
在debug模式下可以查看对象的虚函数表指针,和虚函数表内容。可以看到:
派生类的func()覆盖了基类的func().
虽然基类的同名虚函数func(int)被隐藏,但是在派生类虚函数表中依然可以看到基类的func(int)虚函数的指针。
但是可以看到的是,派生类中自定义的func1()却没有显示出来,因此vc查看到的是不完整的。
gdb查看
在vc下面查看派生类自定义的虚函数无法看到,因此可以在linux下利用gdb来查看:同时可以利用:info line #linenum 来查看函数的地址
上面可以很清楚的看到派生类A中的虚函数表的内容,同时也可以看到函数的具体地址。
对象a的虚函数表:
结论
虽然派生类会隐藏基类中的虚函数,但是在派生类的虚函数表中依然存在基类的虚函数指针,只不过在编译检查时,基于隐藏的原则提示编译错误。借助于VC可以查看虚函数表,但是对于子类的虚函数表显示并不完整, 只会显示基类的虚函数的部分。
基于gdb我们可以查看整个虚函数表的内容,注意的是系统的位数不同函数指针所占的字节也不同。
本文只是查看了单继承的虚函数表,借助gdb我们就可以查看具体的复杂继承情况。
参考:
/article/2551883.html
/article/5196722.html
相关文章推荐
- 51nod 1066 Bash游戏
- hdu2072——单词数(STL,set)
- [置顶] 【JZSC2017】~【NOIP2017】
- 基于高斯方法的图像模糊
- java 多线程
- yii2设置伪静态
- 设计模式笔记---简单工厂
- linux下使用GDB调试程序
- Android基础 | 创建新的Activity
- 关闭ubuntu的防火墙
- C经典 内存分配解析
- Java学习笔记之输入输出流(一) File、文件字节流、文件字符流
- C++的强制类型转换
- 关于九种求和方法
- matlab 随机函数的使用
- BF算法的实现
- 【Coding算法导论】第4章:最大子数组问题
- 线段树练习2
- 【Coding算法导论】第4章:最大子数组问题
- JavaScript -- 制作简易瀑布流