C++ 虚函数通常使用虚函数表实现。
多态的原理
通常,编译器处理虚函数的原理是:给每个类实例添加一个隐藏成员,这个隐藏成员保存了一个指向函数指针数组的指针,该数组称为虚函数表,我们用 vtable
指代它,我们用 vptr
指代指向该数组的指针。 而 vptr
位于类实例内存的最前面的位置,由于 vtable
为存储函数指针的数组,故我们可以通过指针操作获得 vtable
的元素,并调用。
代码示例
#include <iostream>
// 定义类时,虚函数的声明顺序,决定了 vtable 内函数指针的顺序
class Base {
public:
Base() = default;
virtual void fun1() { std::cout << "Base::fun1()" << std::endl; }
virtual void fun2() { std::cout << "Base::fun2()" << std::endl; }
virtual ~Base() = default;
};
class Derived : public Base {
public:
Derived() = default;
virtual void fun1() { std::cout << "Derived::fun1()" << std::endl; }
virtual void fun2() { std::cout << "Derived::fun2()" << std::endl; }
virtual ~Derived() = default;
};
typedef void (*fun)(); // 函数指针
int main() {
Base* b = nullptr;
size_t* vptr = nullptr; // vptr 位于类实例内存的头部
size_t* vtable = nullptr; // vtable 即为函数指针数组
fun pfun = nullptr;
b = new Base();
vptr = (size_t*)b; // 获取 vptr 地址
vtable = (size_t*)*vptr; // 获取 vtable 地址
pfun = (fun)*vtable; // 获取指针数组的第一元素
pfun();
pfun = (fun) * (vtable + 1); // 获取指针数组的第二元素
pfun();
delete b;
b = new Derived();
vptr = (size_t*)b;
vtable = (size_t*)*vptr;
pfun = (fun)*vtable;
pfun();
pfun = (fun) * (vtable + 1);
pfun();
delete b;
return 0;
}