本文主要是介绍【C++】深入理解虚表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
简述虚表
虚表是记录本类中所有虚函数地址的一个表格。
虚表的内存结构
如下,我们设计了一个类,存在两个虚函数。
class A
{
public:virtual void fun1() {cout << "a" << endl;}virtual void fun2() {cout << "b" << endl;}
};
通过A实例化a,再看看a的内存结构。_vfptr就是虚表!!!可以把它看成存放了void*型对象的数组。 而_vfptr的两个成员分别代表fun1函数的指针和fun2函数的指针。这也正好验证了虚表就是记录本类中所有虚函数地址的一个表格。
获取虚表
经测试,通过如下代码可以获取虚表。
A a;
int *vptr = (int *)(*(int*)(&a));
再看内存监视信息,我们发现vptr的地址和_vfptr的地址一样,测试通过-v-
通过虚表调用虚函数
看上图,我们发现vptr[0]的值与虚表中第一个成员的值相等。那我么可以通过吧vptr[0]转换成fun1函数类型,来达到不可告人的目的。
typedef void (*Fun)(void);
Fun f1 = (Fun)vptr[0];
f1();
调用后,控制台打印“a”,象征着我们尝试成功。同理,我们也可以通过vptr[1]来调用fun2函数。
虚表中虚函数的排列顺序
通过上图,我们很容易就可以看出,是按函数的声明顺序排列的。虽然简单,但还是请记住了。不要一不小心调用了虚析构函数,程序直接崩溃。
继承中覆盖虚函数
我们再设计一个AA类,继承A,并且在A中实现fun1()函数。
class AA : public A
{
public:virtual void fun1() {cout << "aa" << endl;}
};
通过AA实例化aa,再看看aa的内存结构。
发现没有,AA::fun1。我们只实现了fun1(),虚表中的fun1函数使用的是AA类中的实现,而fun2函数使用的时A类中的实现。有没有隐隐约约嗅到动态绑定的味道?
结语
看完本篇文章,是不是对神秘莫测的虚表有了更一步的认识。
这里只是从内存层面讲解了虚表,欲了解更加透彻,请查阅“继承”,“动态绑定”等知识。
这篇关于【C++】深入理解虚表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!