前言
C++ 中的虚函数是实现多态的核心机制。简单来说,多态允许父类指针指向子类实例,并通过父类指针调用实际子类的成员函数。这种技术让代码具有了泛型能力,试图用不变的接口实现可变的算法。
关于虚函数的基本用法,相关书籍已有详尽介绍。本文重点从底层实现机制出发,剖析虚函数表(Virtual Table)是如何工作的,希望能为大家提供一个清晰的视角。
虚函数表基础
虚函数通过一张虚函数表来实现,简称 V-Table。这张表存储了类中所有虚函数的地址,解决了继承和覆盖的问题,确保调用时能准确定位到实际的函数实现。
在含有虚函数的类实例中,这张表被分配在对象内存的最前面。这是为了保证访问性能最高,尤其是在多层或多重继承的情况下。这意味着我们可以通过对象实例的地址找到虚函数表,遍历其中的函数指针并调用相应函数。
假设我们有这样一个基类:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
我们可以通过强制类型转换来观察虚函数表的布局。注意,以下操作涉及底层内存访问,仅用于理解机制,生产环境慎用:
typedef void (*Fun)(void);
Base b;
Fun pFun = NULL;
// 获取虚函数表地址
cout << "虚函数表地址:" << (int*)(*(int*)&b) << endl;
// 获取第一个虚函数地址并调用
pFun = (Fun)((int*)(*(int*)&b));
pFun(); // 输出 Base::f
运行结果会显示虚函数表的地址以及第一个函数 Base::f 的入口地址。通过偏移量,我们可以依次访问后续虚函数:
(Fun)((int*)(*(int*)&b) + );
(Fun)((*)(*(*)&b) + );
(Fun)((*)(*(*)&b) + );


