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

15.虚函数

想彻底搞懂虚函数这个C++核心概念(也是QT多态的基础),用新手能听懂的语言,结合QT的例子一步步讲清楚,包括虚函数的定义、作用、用法和底层逻辑(简化版)。

先给新手的通俗定义

虚函数(Virtual Function)是C++中加了virtual关键字的成员函数,核心作用是:让父类的指针/引用调用这个函数时,能自动执行子类重写后的版本,而不是父类自己的版本

打个比方:

  • 父类(动物)定义虚函数叫()virtual void 叫() = 0;
  • 子类(猫)重写叫()void 叫() override { 喵~ }
  • 子类(狗)重写叫()void 叫() override { 汪~ }
  • 用父类指针动物 *p = 猫→ 调用p->叫()→ 输出“喵~”;
  • 用父类指针动物 *p = 狗→ 调用p->叫()→ 输出“汪~”;

如果没有virtual,不管指针指向猫还是狗,调用p->叫()都会执行父类的版本(但父类可能没实现,直接报错)。

虚函数的核心语法(结合QT例子)

我们用之前通信设备的例子,拆解虚函数的关键语法:

1. 父类中声明虚函数
class CommDevice : public QObject { Q_OBJECT public: // ① 纯虚函数(=0):强制子类必须重写,父类无实现 virtual bool sendData(const QString &data) = 0; // ② 普通虚函数:父类有默认实现,子类可选重写 virtual void showType() { qDebug() << "我是通用通信设备"; } };
  • virtual:必须加在函数声明前,标记这是虚函数;
  • =0:表示“纯虚函数”,父类不写实现,子类必须重写(父类也因此变成“抽象类”,不能直接创建对象);
  • 普通虚函数(不加=0):父类有默认实现,子类可以选择重写或不重写。
2. 子类中重写虚函数
class SerialDevice : public CommDevice { public: // 重写纯虚函数(必须加 override,新手必加!) bool sendData(const QString &data) override { qDebug() << "串口发送:" << data; return true; } // 重写普通虚函数(可选) void showType() override { qDebug() << "我是串口设备"; } };
  • override:C++11新增关键字,不是必须,但强烈建议加
    • 作用:告诉编译器“我要重写父类的虚函数”,如果父类没有这个虚函数(比如函数名写错、参数不一致),编译器会直接报错,避免新手踩坑;
    • 比如你把sendData写成sendDatas,加了override后编译器会提示“没有可重写的虚函数”,立刻发现错误。
3. 调用虚函数(多态的体现)
// 父类指针指向子类对象 CommDevice *device = new SerialDevice; // 调用虚函数:执行子类的版本 device->sendData("测试数据"); // 输出“串口发送:测试数据” device->showType(); // 输出“我是串口设备” delete device;

没有虚函数会怎么样?(反例,新手对比理解)

如果父类的sendData不加virtual

// 错误示范:父类无virtual class CommDevice { public: // 无virtual bool sendData(const QString &data) { qDebug() << "父类通用发送"; return true; } }; class SerialDevice : public CommDevice { public: // 看似重写,实际是子类新定义的函数 bool sendData(const QString &data) { qDebug() << "串口发送"; return true; } }; // 调用结果 CommDevice *device = new SerialDevice; device->sendData("测试"); // 输出“父类通用发送”(不是串口版本!)

原因:没有virtual时,编译器会根据“指针的类型”(CommDevice*)调用函数,而不是“指针指向的对象类型”(SerialDevice)——这就是“静态绑定”,而虚函数是“动态绑定”。

虚函数的底层逻辑(简化版,新手不用深究,了解即可)

C++为每个有虚函数的类创建了一张“虚函数表(vtable)”:

  1. 父类的虚函数表存着自己的虚函数地址;
  2. 子类重写虚函数后,会把自己的函数地址替换到虚函数表中;
  3. 每个对象都有一个隐藏的“虚表指针(vptr)”,指向自己类的虚函数表;
  4. 调用虚函数时,编译器会通过vptr找到虚函数表,再执行对应地址的函数——所以能“动态”找到子类的实现。

QT的QObject类本身就有大量虚函数(比如event()paintEvent()),这也是QT控件能重写事件处理函数的原因。

虚函数的使用规则(新手必记)

  1. 声明位置virtual只需要加在父类的函数声明处(.h文件),子类重写时可以不加,但override必须加;
  2. 析构函数必须加virtual:如果父类指针指向子类对象,删除指针时,没有虚析构会导致子类析构函数不执行,内存泄漏!
// 正确写法:父类虚析构 class CommDevice { public: virtual ~CommDevice() {} // 虚析构 };
  1. 纯虚函数 vs 普通虚函数
    • 纯虚函数(=0):父类无实现,子类必须重写,父类变成抽象类(不能创建对象);
    • 普通虚函数:父类有默认实现,子类可选重写,父类可以创建对象(如果没有其他纯虚函数);
  1. 函数签名必须一致:重写的函数名、参数类型/个数、const修饰符必须和父类完全一致,否则编译器认为是新函数,不会触发多态(加override会帮你检查)。

总结

  1. 虚函数的核心作用:实现C++的动态绑定(多态),让父类指针/引用能调用子类的重写函数;
  2. 关键语法:父类加virtual,子类加override
  3. QT中的应用:QT的QObject子类(如窗口、控件)的事件函数(paintEventmousePressEvent)、通信类的核心接口(如之前的sendData)都是虚函数,是QT多态编程的基础;
  4. 新手避坑:重写虚函数必加override,父类析构必加virtual,函数签名必须和父类一致。

虚函数是QT面向对象编程的核心,掌握它就能理解为什么QT的控件能重写事件、为什么通信基类能适配串口/TCP等不同设备——本质都是虚函数实现的多态。

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

相关文章:

  • 小程序毕设项目推荐-基于SpringBoot与微信小程序的在线预约挂号系统医生信息管理、科室管理、号源管理、预约挂号【附源码+文档,调试定制服务】
  • 小程序毕设选题推荐:基于springboot+vue实现的数据资产管理系统基于springboot+小程序的社区资产管理app设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 小程序毕设项目推荐-基于SpringBoot的机票预订系统的设计与实现基于springboot+小程序的航空机票预订系统设计与实现【附源码+文档,调试定制服务】
  • CentOS7 彻底卸载所有JDK/JRE + 重新安装JDK8(实操完整版,解决kafka/jps报错)
  • 小程序毕设选题推荐:基于springboot+小程序的航空机票预订系统设计基于SpringBoot的机票预订系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 计算机小程序毕设实战-基于微信小程序的机票预订平台的设计与实现基于springboot+小程序的航空机票预订系统设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 咕-spring注解--注入-监控-赋值
  • windows11系统 资源管理器卡死问题记录
  • 821. 跳台阶
  • 不用外包省成本!美业老板用 AI 工具做年货节海报,闭眼冲
  • 小程序毕设项目:基于springboot+小程序的航空机票预订系统设计与实现(源码+文档,讲解、调试运行,定制等)
  • 基于Springboot智慧公寓管理系统【附源码+文档】
  • win11 文件管理器卡死问题记录
  • Chrome浏览器原生Gemini AI 功能开启指南(2026年实测)
  • 基于51单片机的步进电机控制系统
  • 小程序毕设项目:基于springboot+小程序的医院挂号系统设计与实现(源码+文档,讲解、调试运行,定制等)
  • 小程序计算机毕设之基于SpringBoot的机票订票管理系统的设计与实现基于springboot+小程序的航空机票预订系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • 基于单片机的多功能电子钟设计
  • 飞牛漏洞焦虑?别瞎折腾WAF了!用Lucky五步搞定“防爬墙”
  • 【毕业设计】基于springboot+小程序的医院挂号系统设计与实现(源码+文档+远程调试,全bao定制等)
  • 10327_基于SpringBoot的视频剪辑咨询网站
  • 小程序毕设项目推荐-基于springboot+小程序的城市公交查询系统设计与实现【附源码+文档,调试定制服务】
  • 小程序毕设选题推荐:基于springboot+小程序的城市公交查询系统设计与实现基于springboot+vue的微信小程序的城市公交查询系统的【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 计算机小程序毕设实战-基于springboot+小程序的城市公交查询系统设计与实现基于SpringBoot+微信小程序的公交信息在线查询系统管理【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 33-学习笔记尚硅谷数仓搭建-DWS层交易域用户粒度订单表分析及设计代码
  • 合集-OICPC 做题记录
  • 【课程设计/毕业设计】基于springboot+小程序的城市公交查询系统设计与实现基于微信小程序的城市公交查询系统的设计与实现【附源码、数据库、万字文档】
  • 我终于靠飞算JavaAI,摆脱了“额度焦虑”
  • 合集-OICPC做题记录
  • 小程序毕设项目:基于springboot+小程序的城市公交查询系统设计与实现(源码+文档,讲解、调试运行,定制等)