前言
在 C++ 中,多态是面向对象编程的核心特性之一。虽然语法层面虚函数、重写、纯虚函数等概念相对直观,但很多开发者对底层的运行机制仍存在疑问:为什么带虚函数的类 sizeof 会变大?基类指针如何找到派生类的函数?虚表与虚指针存在哪里?
本文将从内存布局、对象模型及汇编视角出发,彻底讲透 C++ 多态的底层机制。
一、虚函数与普通函数的区别
通过一个经典问题切入:下面编译为 32 位程序的运行结果是什么?
class Base {
public:
virtual void Func1() { cout << "Func1()" << endl; }
protected:
int _b = 1;
char _ch = 'x';
};
int main() {
Base b;
cout << sizeof(b) << endl;
return 0;
}
正常类成员函数不占用对象内存空间,仅数据成员计入大小。该类的数据成员为 int (4 字节) + char (1 字节),按内存对齐后应为 8 字节。但实际运行结果为 12 字节。
这是因为编译器在含有虚函数的类对象中隐藏插入了一个指针,称为虚函数表指针(vptr)。每个包含虚函数的类对象至少有一个 vptr,用于指向该类所有虚函数的地址集合——即虚函数表(vtable)。

二、多态的实现原理
多态是如何实现的
当满足多态条件(基类指针或引用 + 调用虚函数)时,底层不再由编译时确定函数地址,而是运行时通过对象的虚表查找对应函数地址。
例如,ptr->BuyTicket() 调用:
- 若
ptr指向Person对象,则调用Person::BuyTicket。 - 若
ptr指向Student对象,则调用Student::BuyTicket。
关键在于:调用哪个函数取决于 ptr 指向的实际对象类型,而非指针本身的声明类型。
class {
:
{ cout << << endl; }
:
string _name;
};
: Person {
:
{ cout << << endl; }
:
string _id;
};
: Person {
:
{ cout << << endl; }
:
string _codename;
};
{
ptr->();
}
{
Person ps;
Student st;
Soldier sr;
(&ps);
(&st);
(&sr);
;
}



