多态的理解,虚函数表的理解

#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();

执行流程(动态绑定、多态)

  1. plane中保存的堆对象地址,找到对象头部__vfptr
  2. __vfptr指向 AirBus 虚函数表
  3. fly()取虚表[0],调用AirBus::fly → 输出:空中客车在飞行
  4. 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. 重点回顾单继承关键点

  1. 单继承,无论多少个 virtual,对象永远只有 1 个__vfptr
  2. 子类 override 重写虚函数,不去修改对象里的__vfptr 本身,而是修改虚表里面存储的函数地址
  3. override只是编译期检查:确保父类有这个虚函数,不会改变内存布局;
  4. VS 调试看到两个__vfptr,是调试器 UI 重复展开,堆内存物理上就一份;
  5. 只有多重继承、两个互不相关父类各自带虚函数,对象内存才出现多份独立__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才完整释放子类资源
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值