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

多态--总结复习巩固

一、多态核心概念1.定义多态同一接口不同实现一个行为不同对象做不同事。1.1静态多态编译器发生时机编译时期确定调用哪个函数实现方式函数重载、运算符重载、模板底层原理名字修饰特点效率高但灵活性差1.2动态多态运行期发生时机运行期确定调用哪个函数实现方式虚函数继承底层原理虚函数表vtable)虚函数指针vptr特点灵活性高但有一定性能开销2.动态多态三要素1.继承关系2.基类有虚函数子类重写3.基类指针/引用调用虚函数二、静态多态的底层原理名字修饰静态多态之所以能在编译期确定函数调用是因为 C 编译器会对函数名进行 修饰将函数的参数类型、个数、顺序等信息编码到函数名中。编译后的函数名GCC编译器void func(int) 被修饰为_Z4funcivoid func(double) 被修饰为_Z4funcd名字修饰规则GCC_Z:C函数的统一前缀4函数名func的长度func原函数名iint类型参数ddouble类型参数三、动态多态的核心虚函数表与虚函数指针动态多态的实现依赖于两个关键数据结构虚函数表和虚函数指针。1.虚函数表是一个静态的、只读的函数指针数组每个含有虚函数的类包括其他派生类都有且仅有一个虚函数表表中存储的是该所有虚函数的地址虚函数表在编译期生成存储在程序的只读数据段2.虚函数指针是一个指向虚函数表的指针每个含有虚函数表的类的对象都有一个虚函数指针位于对象内存布局的最开头GCC和MSVC都是如此在构造函数中被初始化指向该类的虚函数表四、单继承下的多态实现代码示例1编译器做了什么对于Base类编译器生成了一个虚函数表里面按声明顺序存放两个虚函数地址Base::func1, Base::func2在每个Base对象中插入一个虚指针指向这个虚表对于Derived类由于它重写了func1并新增了func4编译器生成一个新的虚表内容为第一个槽位Derived::func1(覆盖了基类的func1)第二个槽位Base::func2(未重写保留基类版本)第三个槽位Derived::func4(新增的虚函数)在每个Derived对象中插入的vptr指向这个新虚表。关键无论对象如何构造vptr在构造函数执行时被正确初始化。基类部分构造时vptr指向基类虚表派生类构造函数执行后vptr被更新为派生类虚表。2内存图1、对象的内存布局2、虚表的内部结构五、单继承下的多态实现主函数调用编译器将ptr-func1()转换为类似以下的伪代码步骤1.取vptr从 ptr 指向的对象头部取出 vptr 的值。vptr *( (void***)ptr ); // ptr 指向的对象的第一个8字节64位系统就是 vptr2.定位虚表槽位由于 func1 是 Base 的第一个虚函数编译器知道它在虚表的偏移为 0索引0。func_ptr vptr[0]; // 取虚表第一个槽位的内容即函数地址3.调用(*func_ptr)(ptr); // 调用该函数并传入 this 指针即 ptr因为 ptr 实际指向一个 Derived 对象其 vptr 指向 Derived 虚表而该虚表的第一个槽位被替换成了 Derived::func1所以最终调用的是派生类的版本。如果是非虚函数 ptr-func3()编译器根本不去查虚表直接在编译期确定地址调用 Base::func3静态绑定。为啥ptr-func2() 调用的是基类版本因为 Derived 没有重写 func2所以 Derived 虚表的第二个槽位里存放的仍然是 Base::func2。过程同上vptr[1] 取出这个地址最终执行 Base::func2。这正是虚函数“非重写则继承基类行为”的底层原理。六、多重继承下的多态实现1B 和 C 各自独立存在时B 类每个 B 对象内部有一个 vptr指向 B 的虚表表里是 B::b_func1 和 B::b_func2。C 类每个 C 对象内部有一个 vptr指向 C 的虚表表里是 C::c_func1 和 C::c_func2。2当 A 同时继承 B 和 C 时A的对象内部包含B 子对象和C 子对象两部分还有自己的成员。因为 B 和 C 都引入了虚函数所以 A 对象内部会嵌入两个 vptr第一个 vptr位于 B 子对象的开头指向“融合了 B 接口和 A 新增虚函数”的虚表简称A-in-B 虚表。第二个 vptr位于 C 子对象的开头指向“融合了 C 接口”的虚表简称A-in-C 虚表通常不包含 A 新增的虚函数a_func 一般只挂在第一张表上。A 对象的内存布局两张虚表的内容A-in-B 虚表对应 B 子对象A-in-C 虚表对应 C 子对象3调用过程验证两个虚指针的作用场景1通过 B 基类指针调用pb 指向 A 对象的起始地址即 B 子对象头部通过第一个 vptr 找到 A-in-B 虚表取第0槽位得到 A::b_func1调用正确。场景2通过 C 基类指针调用这里发生了指针调整new A() 返回的是整个 A 对象的地址即 B 子对象地址但赋值给 C* 时编译器会自动调整 pc 的值让它指向 A 对象内部的C 子对象开头也就是第二个 vptr 的位置。然后通过第二个 vptr 找到 A-in-C 虚表取第0槽位得到 A::c_func1调用正确。七、纯虚函数与抽象类纯虚函数的声明virtual void func() 0;纯虚函数的底层实现纯虚函数在虚函数表中对应的位置存储的是0GCC或指向__cxa_pure_virtual函数的指针MSVC如果尝试调用纯虚函数会触发运行时错误含有纯虚函数的类是抽象类不能实例化对象派生类必须重写所有纯虚函数才能实例化八、多态的性能开销与优化1.性能开销内存开销每个对象多一个虚函数指针8 字节64 位系统时间开销虚函数调用比普通函数调用多两次内存访问无法内联虚函数调用是间接调用编译器通常无法内联2.优化方法避免不必要的虚函数只有需要多态的函数才声明为 virtualfinal 关键字C11 引入的 final 关键字可以禁止类被继承或函数被重写编译器可以将虚函数调用优化为直接调用CRTP奇异递归模板模式用模板实现静态多态完全消除运行时开销
http://www.zskr.cn/news/1328172.html

相关文章:

  • 90天小白进阶大模型工程师:从神经网络到Agent实战(收藏版)
  • 告别环境报错!YOLOv5 v7.0 + PyCharm 2023 完整配置流程与项目实战
  • 郑州黄金手镯回收纯银回收白金回收50分钻石回收二手钻石回收本地排名正规门店专业推荐哪家靠谱二手哪家强 - 检测回收中心
  • Flowframes:AI视频插帧工具让你的视频流畅度翻倍
  • RimWorld模组管理终极指南:3步掌握RimSort智能排序,告别游戏崩溃烦恼
  • 告别手动填表!用Python脚本5分钟搞定DSSAT模型批量模拟(附源码)
  • 集成测试实战
  • 从“让大模型回答问题“到智能决策:LangGraph 构建 AI Agent 的核心奥秘
  • 从查重到降 AIGC,2026 年 9 款论文工具横评:Paperxie 领衔,谁才是本科生的 “熬夜救星”?
  • 中山黄金吊坠回收同城白银回收同城铂金回收钻石首饰回收本地贵金属回收本地排名正规门店专业推荐哪家靠谱二手哪家强 - 检测回收中心
  • find命令的-exec参数的特殊语法{} +和{} \
  • MeMo:当记忆本身变成一个模型
  • 建筑空调系统案例数据驱动评价方法【附程序】
  • 一份给公建业主的自动门厂家挑选指南 - 速递信息
  • 天津黄金手镯回收纯银回收白金回收50分钻石回收二手钻石回收本地排名正规门店专业推荐哪家靠谱二手哪家强 - 检测回收中心
  • 在可重复读隔离级别下,事务通过第一次SELECT(快照读)生成了Read View,中间执行了UPDATE、INSERT等操作,会更新这个Read View吗?
  • Windows 11 LTSC 24H2 如何一键安装微软商店?3分钟解决方案揭秘
  • 中山万足金回收银戒指回收铂金戒指回收碎钻回收奢侈品首饰回收高价多少钱一克同城价格查询上门上门估价闲置变现转让靠谱权威排行榜 - 检测回收中心
  • Android Studio中文界面汉化教程:3步实现母语开发环境
  • 杭州避暑亲子好去处:OMG 心跳乐园,溶洞避暑 + 亲子玩乐一站式搞定 - 博客湾
  • 告别卫生间反复渗漏返修 防水维修行业企业选购指南 - 资讯焦点
  • 银川黄金吊坠回收同城白银回收同城铂金回收钻石首饰回收本地贵金属回收本地排名正规门店专业推荐哪家靠谱二手哪家强 - 检测回收中心
  • 中山足金回收银手镯回收PT990铂金回收钻石戒指回收旧首饰回收高价多少钱一克同城价格查询上门上门估价闲置变现转让靠谱权威排行榜 - 检测回收中心
  • 原神帧率解锁完整指南:5分钟突破60帧限制,畅享丝滑游戏体验
  • 2026年台球杆选购指南:实测推荐五大优质销售平台 - 速递信息
  • 【例题2】The XOR Largest Pair(信息学奥赛一本通- P1472)
  • Windows包管理器Winget一键安装指南:3分钟解决官方安装难题
  • Microsoft 365 Copilot交互优化深度解析:F6一键调出AI,企业AI办公迎来拐点?
  • 从车间到桌面:5公斤负载6轴机器人DIY指南(附详细传动结构图)
  • 别再手动算奇偶校验了!聊聊7系列FPGA内置ECC的那些“隐藏”用法与性能取舍