#include <iostream>
using namespace std;
class Plane
{
public:
virtual void fly()
{
cout << "灰机正在飞行" << endl;
}
virtual void showInfo()
{
cout << "灰机正在展示" << endl;
}
};
class AirBus : public Plane
{
public:
void fly() override
{
cout << "空中客车在飞行" << endl;
}
void showInfo() override
{
cout << "空中客车正在展示" << endl;
}
};
class J20 : public Plane
{
public:
void fly() override
{
cout << "J20正在隐形战斗寻航" << endl;
}
};
int main()
{
Plane* plane = new AirBus;
return 0;
}

分析
代码逐段 + 内存布局 + 调用逻辑完整解析
1. 内存情况(64 位 MSVC)
new AirBus 在堆上创建一个 AirBus 对象
- 单继承,只有一份
__vfptr(虚函数表指针,占 8 字节) - 对象没有任何成员变量,所以堆上对象总大小:
8字节
堆内存(AirBus 对象)
堆地址
┌────────────────────┐
│ __vfptr │───→ AirBus 的虚函数表 vftable
└────────────────────┘
AirBus 虚函数表 vftable
表格
| 下标 | 存放函数地址 |
|---|---|
| [0] | &AirBus::fly |
| [1] | &AirBus::showInfo |
虚函数本身代码放在代码段,不在堆对象里;对象只存虚表指针。
栈上变量 Plane* plane:
- 这是指针变量,栈上占 8 字节
- 保存堆上 AirBus 对象的起始地址。
注意:指针的类型是
Plane*,但它存的是实打实 AirBus 对象的堆地址。 VS 调试窗口看到:plane Plane* {AirBus},代表静态类型 Plane*,实际动态类型 AirBus。
2. 补充两条调用看效果
plane->fly();
plane->showInfo();
执行流程(动态绑定、多态)
- 拿
plane中保存的堆对象地址,找到对象头部__vfptr __vfptr指向 AirBus 虚函数表fly()取虚表[0],调用AirBus::fly→ 输出:空中客车在飞行showInfo()取虚表[1],调用AirBus::showInfo→ 输出:空中客车正在展示
3. 对比 J20
Plane* p2 = new J20;
p2->fly(); // J20重写fly,虚表[0] = &J20::fly,输出J20正在隐形战斗寻航
p2->showInfo(); // J20没有重写showInfo,虚表[1] = &Plane::showInfo,输出灰机正在展示
J20 虚函数表
表格
| 下标 | 函数地址 |
|---|---|
| [0] | &J20::fly |
| [1] | &Plane::showInfo |
4. 重点回顾单继承关键点
- 单继承,无论多少个 virtual,对象永远只有 1 个
__vfptr; - 子类 override 重写虚函数,不去修改对象里的__vfptr 本身,而是修改虚表里面存储的函数地址;
override只是编译期检查:确保父类有这个虚函数,不会改变内存布局;- VS 调试看到两个
__vfptr,是调试器 UI 重复展开,堆内存物理上就一份; - 只有多重继承、两个互不相关父类各自带虚函数,对象内存才出现多份独立
__vfptr。
5. 内存大小汇总(64 位 MSVC)
表格
| 类型 | sizeof | 说明 |
|---|---|---|
| Plane | 8 | 仅__vfptr |
| AirBus | 8 | 继承,无新增成员变量,一份__vfptr |
| J20 | 8 | 继承,无新增成员变量,一份__vfptr |
| Plane* | 8 | 指针本身大小,和指向什么对象无关 |
6. 内存泄漏提醒
代码中new AirBus堆分配,没有写delete plane;,程序退出前没有释放堆内存,造成内存泄漏。
cpp
int main()
{
Plane* plane = new AirBus;
delete plane; //释放堆对象
return 0;
}
⚠这里有个重要知识点:基类析构函数如果不是 virtual,delete 基类指针不会正确调用子类析构!
给你演示坑:
cpp
class Plane
{
public:
virtual void fly(){}
//~Plane(){} //普通析构,不是virtual,delete plane;只会调用Plane析构,不会调用AirBus析构!
virtual ~Plane(){} //✅虚析构,多态delete才完整释放子类资源
};

3819

被折叠的 条评论
为什么被折叠?



