从零开始也不怕!友为合同管理系统「传统起草」全解析:任何非标合同都能轻松搞定
2026/4/22 16:39:24
想彻底搞懂虚函数这个C++核心概念(也是QT多态的基础),用新手能听懂的语言,结合QT的例子一步步讲清楚,包括虚函数的定义、作用、用法和底层逻辑(简化版)。
虚函数(Virtual Function)是C++中加了virtual关键字的成员函数,核心作用是:让父类的指针/引用调用这个函数时,能自动执行子类重写后的版本,而不是父类自己的版本。
打个比方:
叫():virtual void 叫() = 0;叫():void 叫() override { 喵~ }叫():void 叫() override { 汪~ }动物 *p = 猫→ 调用p->叫()→ 输出“喵~”;动物 *p = 狗→ 调用p->叫()→ 输出“汪~”;如果没有virtual,不管指针指向猫还是狗,调用p->叫()都会执行父类的版本(但父类可能没实现,直接报错)。
我们用之前通信设备的例子,拆解虚函数的关键语法:
class CommDevice : public QObject { Q_OBJECT public: // ① 纯虚函数(=0):强制子类必须重写,父类无实现 virtual bool sendData(const QString &data) = 0; // ② 普通虚函数:父类有默认实现,子类可选重写 virtual void showType() { qDebug() << "我是通用通信设备"; } };virtual:必须加在函数声明前,标记这是虚函数;=0:表示“纯虚函数”,父类不写实现,子类必须重写(父类也因此变成“抽象类”,不能直接创建对象);=0):父类有默认实现,子类可以选择重写或不重写。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后编译器会提示“没有可重写的虚函数”,立刻发现错误。// 父类指针指向子类对象 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)”:
QT的QObject类本身就有大量虚函数(比如event()、paintEvent()),这也是QT控件能重写事件处理函数的原因。
virtual只需要加在父类的函数声明处(.h文件),子类重写时可以不加,但override必须加;// 正确写法:父类虚析构 class CommDevice { public: virtual ~CommDevice() {} // 虚析构 };=0):父类无实现,子类必须重写,父类变成抽象类(不能创建对象);override会帮你检查)。virtual,子类加override;QObject子类(如窗口、控件)的事件函数(paintEvent、mousePressEvent)、通信类的核心接口(如之前的sendData)都是虚函数,是QT多态编程的基础;override,父类析构必加virtual,函数签名必须和父类一致。虚函数是QT面向对象编程的核心,掌握它就能理解为什么QT的控件能重写事件、为什么通信基类能适配串口/TCP等不同设备——本质都是虚函数实现的多态。