当前位置: 首页 > news >正文

C++进阶 多态

一.多态的概念

多态是面向对象编程的一大核心特性,意思是 同一个操作作用于不同的对象,可以有不同的行为。用一句话总结:

同样的接口,不同的实现。

举个直观例子:

动物有一个 叫() 方法。

不同动物会叫不同的声音:

狗.叫() → “汪汪”

猫.叫() → “喵喵”

鸟.叫() → “啾啾”

这里,调用 叫() 是一样的,但结果因对象类型不同而不同,这就是多态。

二.多态的定义及实现

1.多态的构成条件

通过 继承 + 虚函数(virtual) 实现

基类函数声明为 virtual

派生类重写(override)该函数

通过 基类指针或引用 调用,执行的是派生类版本

注意:

指针或引用调用才能体现多态;如果是对象本身,会发生 对象切片,多态失效。

基类析构函数也要写 virtual,否则通过基类指针删除派生对象时可能不会调用派生类析构。

2.虚函数

虚函数是实现运行时多态的关键。

在基类中用 virtual 修饰的成员函数称为虚函数。

3.虚函数的重写和覆盖

虚函数的重写/覆盖:派⽣类中有⼀个跟基类完全相同的虚函数(即派⽣类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派⽣类的虚函数重写了基类的虚函数。

这里:

重写了:

4.析构函数的重写

发现:

名字根本不一样

但是一样能构成重写

这是因为编译器内部会把析构函数统一处理为

为什么析构函数要设计成虚函数

如果析构函数不是虚函数

输出: ~Person()

只有基类析构被调用

假设 Student 有动态资源

如果:

只会调用:

那么:

永远不会执行

导致内存泄露

5.override 和 final关键字

1.override

作用

告诉编译器,这个函数必须是在重写(Override)基类的虚函数,如果不是重写,编译器直接报错。

开发者本来想重写:

结果写成

实际上不是重写,而是隐藏,但是编译器没有报错

所以就需要Override来检查重写

编译器立即报错

立即发现问题

2. final

作用

final 表示:

到此为止,不允许继续继承或重写。

final 修饰虚函数

编译失败

final 修饰类

表示Base 是最终类,任何类都不能继承它。

override 和 final 一起使用

含义:

编译报错

6.重载/重写/隐藏的对⽐

三.纯虚函数和抽象类

1.纯虚函数

定义

如果一个函数在基类中没有实现,或者我们希望派生类必须重写它,就可以声明为 纯虚函数。

语法:

纯虚函数特点

必须是虚函数

不能在基类直接实例化对象

可以在派生类重写,也可以在基类提供实现

2.抽象类

定义

含有至少一个纯虚函数的类叫做 抽象类。

特点:

1.不能实例化

2. 可以作为指针或引用类型使用

3. 派生类必须实现所有纯虚函数,否则派生类仍是抽象类

四.虚函数表指针

1. 什么是虚函数表指针(vfptr)

当一个类中存在虚函数时

编译器会偷偷给对象增加一个成员:

这个隐藏成员就是虚函数表指针

作用:指向当前类对应的虚函数表(vftable)。

2.什么是虚函数表

编译器会生成:

vfptr 指向它:

3. vfptr什么时候产生

只要类中有虚函数:

对象中就有vfptr

4. vfptr存在哪里

vfptr存放在对象里面。

注意:

vfptr属于对象

vftable属于类

每个对象都有自己的vfptr,但都指向同一张虚表

5.多态调用过程

五.多态原理

1.多态是如何实现的

通过虚函数表(vftable)和虚函数表指针(vfptr)实现。

2.多态的实现原理

第一步:生成虚函数表

基类

编译器生成:

第二步:对象中增加vfptr

Person对象:

vfptr:

指向虚函数表

第三步:派生类重写虚函数

编译器生成Student虚表

重写后:

对象布局

创建:

3.多态调用过程

普通函数

如果不是virtual:

编译阶段直接确定

这叫静态绑定

虚函数

有virtual:

编译器不会直接决定。

运行时执行:

① 找p指向的对象

② 找对象中的vfptr

③ vfptr找到虚函数表

④ 从虚表中取函数地址

⑤ 调用对应函数

这叫动态绑定

总结

C++多态是通过虚函数表(vftable)和虚函数表指针(vfptr)实现的。

当类中存在虚函数时,编译器会为该类生成一张虚函数表,对象中会保存一个虚函数表指针。

当通过基类指针或引用调用虚函数时,程序在运行时根据对象中的vfptr找到对应的虚函数表,再从虚函数表中找到实际要调用的函数地址,从而实现动态绑定和运行时多态。

六.虚函数表

1.同类型对象共用同一张虚表

创建对象:

每个对象都有自己的vfptr但是都指向同一张虚表

因为虚表属于类,不属于对象。

2.基类和派生类有各自独立的虚表

编译器生成:

不是共用一张表

3.为什么派生类对象只有一个vfptr

会不会:

其实并不会

单继承情况下:只有一个vfptr

4.重写后为什么叫覆盖

Person虚表:

Student虚表:

可惜理解成:

原来:
Person::BuyTicket

被替换成:

Student::BuyTicket

这就是覆盖

5.派生类虚表到底包含什么

Person虚表:

Student虚表:

所以派生类虚表包含:

① 继承的虚函数

② 重写后的虚函数

③ 新增的虚函数

6.虚函数表本质是什么

本质上:

就是函数指针数组

数组里面存的是函数地址,不是函数本身

7.虚函数存在哪

很多人以为虚函数在虚表里面

这是错误

实际上:

8.虚表存在哪

严格来说C++标准没有规定,不同编译器实现不同

VS下虚表存在代码段(常量区)

总结:

1. 类中有虚函数时会生成虚函数表(vftable)。

2. 对象中会保存一个虚函数表指针(vfptr)。

3. 同类型对象共享同一张虚表。

4. 派生类拥有独立的虚表。

5. 重写虚函数时,派生类虚表中的对应函数地址会覆盖基类地址。

6. 虚表本质是函数指针数组。

7. 虚函数代码存放在代码段,虚表一般存放在只读数据区,但标准未规定具体位置。

8. 运行时通过vfptr查找虚表中的函数地址,从而实现多态。

http://www.cnnetsun.cn/news/2689493.html

相关文章:

  • 基于OpenCV与HSV颜色空间的实时目标检测与追踪实战
  • LizzieYzy围棋AI分析工具:从入门到精通的5大实用技巧
  • 网盘直链下载工具实用指南:如何实现多平台文件高效获取
  • 如何在5分钟内掌握Blender VRM插件:从零开始创建虚拟角色完整指南
  • BitCPM-CANN应用场景探索:边缘设备部署与内存优化实践指南
  • 3步定位Windows热键冲突:Hotkey Detective深度解析与应用指南
  • Android Studio中文界面配置终极指南:3步告别英文开发困扰
  • ImageGlass:90+图片格式支持,Windows上最轻量高效的开源图片浏览器解决方案
  • 从零设计PCB:用Eagle打造会发光的Instructables机器人徽章
  • 2026大模型聚合API平台全景测评:核心参数、适用场景、优势盘点
  • ESP32开发进阶:掌握ESP-IDF命令行工具从入门到精通
  • 用UE5 Niagara做个会飘的蒲公英吧!从虚幻商城素材到GPU粒子实战
  • 流量终局:TikTok正在复刻“微信”模式,重塑全球超级应用生态
  • 告别手动标注!用X-AnyLabeling和SAM-HQ模型5分钟搞定图片自动打标(附国内模型下载)
  • Jina Embeddings v2 Base ES:如何快速掌握革命性双语文本嵌入模型
  • 19个Obsidian美化技巧终极指南:让你的笔记软件焕然一新
  • AI-HF_Patch完全指南:3步解锁AI少女游戏的终极体验
  • P3D多屏显示失败?先检查这3个NVIDIA控制面板设置(含Surround配置截图)
  • Legado开源阅读鸿蒙版:打造您的专属无广告数字图书馆
  • 如何为OpenChat-3.5-1210-openmind开发自定义功能:扩展模型能力的完整指南
  • Joy-Con Toolkit:解锁Nintendo Switch手柄隐藏功能的终极指南
  • 从零制作单管音频放大器:用D313晶体管驱动喇叭的实践指南
  • UnrealPakViewer架构解析:300%效率提升的虚幻引擎Pak文件深度分析方案
  • 基于Pinoo与Mblock3的倾斜传感器猜色游戏:事件驱动编程入门实践
  • 5分钟掌握BetterNCM安装器:网易云音乐终极插件框架完整指南
  • 大气层系统(Atmosphere)终极指南:简单5步解锁Switch无限潜能
  • 围棋AI分析神器LizzieYzy:5分钟快速上手的终极指南
  • 从零打造8x8x8 LED光立方:硬件搭建、驱动原理与Arduino编程全解析
  • 原神帧率解锁终极指南:5分钟实现120帧流畅体验
  • 终极微信聊天记录导出备份指南:永久保存你的珍贵回忆