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

C++虚函数陷阱

以下程序输出结果是什么()
A : A->0 B : B->1 C : A->1 D : B->0 E : 编译出错 F : 以上都不正确

#include <iostream> class A { public: virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; } virtual void test() { func(); } }; class B : public A { public: void func(int val = 0) { std::cout << "B->" << val << std::endl; } }; int main(int argc, char* argv[]) { B* p = new B; p->test(); return 0; }

结论:正确答案是 B : B->1
1.分析:在主函数中,创建了指向派生类 B 对象的指针 p,并通过 p 调用 test ()。
由于 B 类中未定义 test () 函数,编译器会根据继承的作用域查找规则,向上查找并调用基类 A 的 test () 函数。
2.虽然 test() 是虚函数,但此处由 B 类指针直接调用,不满足基类指针 / 引用调用的条件,因此 test () 本身不构成多态。
3.那么在 test() 函数内部调用的 this->func () 是否构成多态?
结论:构成多态。
原因:C++ 运行时多态必须通过基类指针或引用调用虚函数,而 A::test() 内部调用 func() 时,本质是通过基类 A 类型的 this 指针完成调用。
4.关于 this 指针的指向,存在两种常见理解分歧:
(1) 认为调用发生在基类 A 的作用域内,因此 this 指向 A 类对象;
(2) 认为由派生类 B 的指针调用 test (),因此 this 指向 B 类对象。
要明确这一点,需要理解 C++ 的继承机制:
派生类对象会完整包含基类子对象,基类成员是派生类对象的组成部分,并非简单拷贝如果只是单纯的拷贝,那么就不会有函数隐藏的概念。

函数隐藏::本质是作用域名字遮蔽:当派生类定义与基类同名函数时,会优先屏蔽基类同名函数,通过派生类对象调用时优先使用派生类版本。
派生类对象由两部分组成:基类成员部分和派生类新增成员部分。调用成员函数时,编译器先在当前派生类中查找,找不到再向基类逐层查找。
5.本程序中,p 调用 test() 时,B 类无 test(),因此查找到基类 A 的 test () 并执行。
虽然执行的是 A::test (),但调用者仍然是 p 指向的 B 类对象,即 this 等价于 B 对象。
在 A::test() 中,this 指针的静态类型为 A*,动态类型为 B*。使用静态类型为 A* 的 this 指针调用虚函数 func (),满足多态三条件:继承关系、派生类重写虚函数、基类指针调用,因此触发动态绑定。
6.由于调用者是 B 对象,进入 A::test() 后,静态类型为 A*、动态类型为 B* 的 this 指针调用 func (),触发多态,函数体最终绑定到派生类 B 的重写版本。
C++ 运行时多态的三要素:存在继承关系、派生类重写基类虚函数、通过基类指针或引用调用虚函数。
在多态调用中,虚函数的默认参数由指针的静态类型决定,函数体实现由对象的动态类型决定。
因此最终调用效果等价于使用 A 类默认参数 val=1,执行 B 类的函数体,

virtual void func(int val = 1) { std::cout << "B->" << val << std::endl; }

输出 B->1。

http://www.jsqmd.com/news/543058/

相关文章:

  • 基于springboot的旅游景点门票信息系统设计与实现-vue
  • Navicat连接密码的AES-CBC加解密实战
  • RimWorld开局定制神器:EdB Prepare Carefully深度应用指南
  • TMS320F28P550SJ9实战解析:Sysconfig高效配置SCI多处理器通信模式
  • Gemini提示词反推教程!“图生图”来了
  • 如何解决CH340串口转USB设备断开连接后重连提示Unable to set the serial port state的问题
  • 朋友圈发图像素太低,被吐槽像座机拍的。调整照片像素,再也不怕被嘲。
  • 3个技巧快速掌握Mermaid在线编辑器:免费制作专业图表终极指南
  • OpenClaw备份策略:Qwen3.5-9B配置与技能的安全保存
  • Python将Parquet文件转换为JSONL格式文件
  • 多代同堂家庭外卖点单指南:宝妈实用备注技巧+全口味适配方案 - 速递信息
  • 刷力扣用for求了无数次数组和?别急,numeric来救急
  • 昆仑通态MCGS与西门子S7-200/200SMART PLC通讯及控制台达变频器技术解析
  • 推荐算法数学概念-向量-矩阵-损失函数-梯度下降
  • 帮老人远程点清淡外卖全攻略:地址设置+餐品选择+优惠指南 - 速递信息
  • 【数据结构与算法】第7篇:线性表(三):单链表的经典面试题(反转、找中间节点)
  • 个人开发者如何高效率APP上架安卓应用市场?软著、备案、资质、审核详解大全,一篇文章讲透流程规则!
  • 选吉他不踩坑:合板、单板、全单材质深度解析,新手看懂这篇就够
  • 42-西门子1200伺服控制5轴程序 程序采用1200系列PLC,项目实现以下功能: (1)
  • vLLM-v0.17.1实操手册:vLLM在Mac M2 Ultra上通过ROCm模拟运行
  • 如何快速回收微信立减金闲置资源?全攻略解析 - 团团收购物卡回收
  • 告别碎片化工具链:用Cube-Studio统一管理你的开源大模型(从ChatGLM到Llama3)
  • 目标检测损失函数进化史:从IoU到EIoU/SIoU/WIoU,YOLOv8性能提升完全指南
  • 【FreeRTOS实战入门】一、从CubeMX到第一个任务:手把手搭建FreeRTOS工程
  • 零成本搞数字化!免费低代码工具(斑斑AI vs 宜搭)测评
  • iOS18适配避坑指南:Xcode16编译报错全解析(含YYCache、ADClient修复方案)
  • 校园外卖配送范围查询及门口自取设置全攻略 - 速递信息
  • YOLOv12学术论文写作:使用LaTeX排版技术报告与实验图表
  • Llama-3.2V-11B-cot效果实测:同一张图不同提问下的CoT推理路径对比分析
  • 带娃宅家点外卖安全健康攻略:从商家筛选到餐品搭配全指南 - 速递信息