一、背景知识
在讲解虚函数的时候,我们知道,如果类中有虚函数,则该类中存在一个虚函数表(V-Table),每个该类的对象都会有一个指针指向该虚函数表,存储这类中虚函数的函数指针,而虚函数表的地址就存在该类对象内存的开始处,目的是为了方便查找虚函数。
在陈浩的技术专栏中写过一篇对C++虚函数表解析很透彻的的文章: C++ 虚函数表解析
这篇文章里对虚函数表的结构做了很透彻的解析,并且配有直观的图片讲解,大家看了以后会比较明白的。这里我们着重讲下通过虚函数表调用虚函数与通过虚函数表(绕过访问权限控制)的代码实现。
二、通过虚函数表调用虚函数
在陈浩大哥的那篇文章里面提到如何通过虚函数表调用函数,并有示例代码如下:
#include<iostream>
#include<string>
using namespace std;
typedef void(*Fun)(void);
class Base {
public:
virtual void f() {
cout << "Base::f()" << endl;
}
virtual void g() {
cout << "Base::g()" << endl;
}
virtual void h() {
cout << "Base::h()" << endl;
}
};
上面是基类代码,Base类中依次有三个虚函数: f(), g(), h(),它们的指针也被依次存储在Base类的V-Table中。下面代码尝试通过V-Table中的虚函数指针来调用虚函数:
int main() {
Base b;
Fun fp = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表第一个虚函数指针地址:" <<(int*)*(int*)(&b) << endl;
for (int i = 0; i != 3; ++i) {
fp = (Fun)*((int*)*(int*)(&b) + i);
fp();
}
return 0;
}
我在自己电脑(64位系统)上运行上述测试代码时,运行结果如下:
虚函数表地址:0x7fff13ec2c80
虚函数表第一个虚函数指针的地址:0x400c70
Base::f()
Segment