news 2026/5/30 15:17:59

14.多态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
14.多态

用QT写一个无界面、新手友好的多态例子,并且详细讲解每一步的逻辑,彻底理解多态的核心含义和实际用法。

先通俗理解多态(新手版)

多态的核心是:同一个接口,不同的实现
就像你去餐厅点“饮料”(统一接口),服务员给你可乐/果汁/茶(不同实现)——你只需要说“要饮料”,不用关心具体是哪种,每种饮料都有自己的“喝法”(不同行为)。
在QT/C++中,多态主要通过虚函数实现:父类定义虚函数(统一接口),子类重写这个函数(不同实现),用父类指针/引用调用时,会自动执行子类的实现。

例子设计思路(无界面,贴近通信场景)

我设计一个极简但完整的多态例子,基于你熟悉的“通信”场景:

  1. 父类(通信设备基类):定义虚函数sendData()(统一接口)。
  2. 子类1(串口设备):重写sendData(),实现串口发送逻辑。
  3. 子类2(TCP设备):重写sendData(),实现TCP发送逻辑。
  4. 测试逻辑:用父类指针指向不同子类对象,调用同一个sendData(),观察不同的执行结果(多态的体现)。

完整代码实现(逐文件讲解)

1. 通信设备基类(commdevice.h)—— 定义统一接口

这是多态的“核心骨架”,用纯虚函数(=0)强制子类实现统一接口,保证所有通信设备都有sendData()方法。

#ifndef COMMDEVICE_H #define COMMDEVICE_H #include <QObject> #include <QString> #include <QDebug> // 通信设备基类(抽象类) class CommDevice : public QObject { Q_OBJECT public: // 构造函数(给父类传参) explicit CommDevice(QObject *parent = nullptr) : QObject(parent) {} // 虚析构函数:必须加!保证子类析构时能正确调用自己的析构 virtual ~CommDevice() { qDebug() << "CommDevice 基类析构"; } // 核心:虚函数(统一接口),子类必须重写 // virtual 关键字是多态的关键! virtual bool sendData(const QString &data) = 0; // 纯虚函数,无实现 }; #endif // COMMDEVICE_H
2. 串口设备子类(serialdevice.h/cpp)—— 重写接口

继承基类,实现串口特有的发送逻辑。

serialdevice.h
#ifndef SERIALDEVICE_H #define SERIALDEVICE_H #include "commdevice.h" // 串口设备子类:继承通信基类 class SerialDevice : public CommDevice { Q_OBJECT public: explicit SerialDevice(QObject *parent = nullptr) : CommDevice(parent) { qDebug() << "SerialDevice 串口设备创建"; } // 重写父类的虚函数(必须加 override 关键字,新手一定要加!防止写错) bool sendData(const QString &data) override; // 串口特有配置(子类扩展的功能) void setSerialPort(const QString &portName) { m_portName = portName; } private: QString m_portName; // 串口名(子类私有数据) }; #endif // SERIALDEVICE_H
serialdevice.cpp
#include "serialdevice.h" // 实现串口的sendData:这是串口特有的发送逻辑 bool SerialDevice::sendData(const QString &data) { qDebug() << "【串口发送】端口:" << m_portName << " 数据:" << data; // 这里可以加真实的QT串口发送代码,新手先简化 return true; // 模拟发送成功 }
3. TCP设备子类(tcpdevice.h/cpp)—— 重写接口

继承基类,实现TCP特有的发送逻辑。

tcpdevice.h
#ifndef TCPDEVICE_H #define TCPDEVICE_H #include "commdevice.h" // TCP设备子类:继承通信基类 class TcpDevice : public CommDevice { Q_OBJECT public: explicit TcpDevice(QObject *parent = nullptr) : CommDevice(parent) { qDebug() << "TcpDevice TCP设备创建"; } // 重写父类的虚函数 bool sendData(const QString &data) override; // TCP特有配置(子类扩展的功能) void setTcpAddr(const QString &ip, int port) { m_ip = ip; m_port = port; } private: QString m_ip; // TCP IP(子类私有数据) int m_port; // TCP 端口 }; #endif // TCPDEVICE_H
tcpdevice.cpp
#include "tcpdevice.h" // 实现TCP的sendData:这是TCP特有的发送逻辑 bool TcpDevice::sendData(const QString &data) { qDebug() << "【TCP发送】IP:Port:" << m_ip << ":" << m_port << " 数据:" << data; // 这里可以加真实的QT TCP发送代码,新手先简化 return true; // 模拟发送成功 }
4. 测试主函数(main.cpp)—— 核心:演示多态

这是最关键的部分,新手重点看这里的多态调用逻辑。

#include <QCoreApplication> #include "commdevice.h" #include "serialdevice.h" #include "tcpdevice.h" // 通用发送函数:接收父类指针,实现多态调用 // 新手重点:这个函数只认父类CommDevice,不知道传入的是串口还是TCP! void sendDataByDevice(CommDevice *device, const QString &data) { device->sendData(data); // 核心:调用父类接口,实际执行子类实现 } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // -------------------------- // 步骤1:创建子类对象 // -------------------------- SerialDevice *serial = new SerialDevice; // 串口对象 serial->setSerialPort("COM3"); // 串口特有配置 TcpDevice *tcp = new TcpDevice; // TCP对象 tcp->setTcpAddr("192.168.1.100", 502); // TCP特有配置 // -------------------------- // 步骤2:多态核心操作 // -------------------------- // 父类指针指向串口子类对象 CommDevice *device1 = serial; device1->sendData("串口数据123"); // 实际执行SerialDevice::sendData // 父类指针指向TCP子类对象 CommDevice *device2 = tcp; device2->sendData("TCP数据456"); // 实际执行TcpDevice::sendData // -------------------------- // 步骤3:更实用的场景:通用函数 // -------------------------- // 同一个函数,传入不同子类对象,执行不同逻辑 sendDataByDevice(serial, "通用函数-串口发送789"); sendDataByDevice(tcp, "通用函数-TCP发送000"); // -------------------------- // 清理内存(新手别漏) // -------------------------- delete serial; delete tcp; return a.exec(); }

代码运行结果(新手重点看)

SerialDevice 串口设备创建 TcpDevice TCP设备创建 【串口发送】端口: COM3 数据: "串口数据123" 【TCP发送】IP:Port: "192.168.1.100" : 502 数据: "TCP数据456" 【串口发送】端口: COM3 数据: "通用函数-串口发送789" 【TCP发送】IP:Port: "192.168.1.100" : 502 数据: "通用函数-TCP发送000" CommDevice 基类析构 CommDevice 基类析构

逐行讲解多态核心逻辑(新手必看)

  1. 父类定义虚函数CommDevice中的virtual bool sendData(...) = 0;是“统一接口”,告诉所有子类:必须实现这个发送方法,但怎么实现我不管。
  2. 子类重写虚函数SerialDeviceTcpDevice都用override重写了sendData,分别实现串口/TCP的发送逻辑——这是“不同实现”。
  3. 父类指针指向子类对象
    • CommDevice *device1 = serial;:父类指针装了串口对象的地址。
    • device1->sendData(...):编译器不会直接调用父类的方法,而是“看指针实际指向的对象”——发现是串口对象,就执行串口的sendData
  1. 通用函数的妙用sendDataByDevice(CommDevice *device, ...)这个函数只接收父类指针,不管传入的是串口还是TCP,都能正确调用对应的发送方法——这就是多态的价值:写一次代码,适配多种场景

新手容易踩的坑(重点提醒)

  1. 忘记加 virtual 关键字:父类的函数如果不加virtual,就不会触发多态,调用的永远是父类的方法(但本例中父类是纯虚函数,会编译报错,反而更安全)。
  2. 重写函数时签名不一致:比如父类是sendData(const QString &),子类写成sendData(QString)——编译器会认为是新函数,不是重写!一定要加override关键字,编译器会帮你检查签名是否一致。
  3. 漏写虚析构:父类析构如果不加virtual,删除父类指针时,子类的析构不会执行,会导致内存泄漏。

总结

  1. 多态的核心:QT/C++中通过virtual虚函数实现,父类定义统一接口,子类重写实现不同逻辑,父类指针/引用调用时自动匹配子类实现。
  2. 关键语法:父类虚函数(virtual)+ 子类重写(override)+ 父类指针指向子类对象。
  3. 实际价值:写通用代码(比如sendDataByDevice)时,不用关心具体是哪种设备,新增UDP设备只需继承基类重写sendData,不用改原有代码(符合开闭原则)。

这个例子是新手能直接跑通的极简版本,没有复杂的QT底层通信逻辑,所有注意力都集中在多态的核心语法和逻辑上,你可以先复制代码跑起来,再逐行修改体会多态的效果。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/30 15:16:14

基于物联网的自动灌溉系统的设计与实现(有完整资料)

资料查找方式&#xff1a; 特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可 编号&#xff1a; CJ-32-2022-013 设计简介&#xff1a; 本设计是基于物联网的自动灌溉系统&#xff0c;主要实现以下功能&#xff1a; 1&#xff0c;OLED显示温湿度和…

作者头像 李华
网站建设 2026/5/30 15:15:26

基于物联网的血压计设计(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;CJ-32-2022-017设计简介&#xff1a;本设计是基于物联网的血压计设计&#xff0c;主要实现以下功能&#xff1a;1&#xff0c;通过OLED显示温度、心率和血压…

作者头像 李华
网站建设 2026/5/30 15:17:53

springboot学习资源推荐系统_开题报告_晓庄

目录 springboot学习资源推荐系统开题报告&#xff08;晓庄&#xff09; 项目技术支持可定制开发之功能亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作 springboot学习资源推荐系统开题报告&#xff08;晓庄&#xff09; 背景与意义 S…

作者头像 李华
网站建设 2026/5/28 14:55:54

Windows IP 配置工具 v1.3 丨绿色便携版

Windows 网络 IP 配置工具 v1.3 是专为 Windows 系统打造的绿色便携网络管理工具&#xff0c;无需安装可直接使用&#xff0c;适配网络管理员与普通用户的网络 IP 配置需求&#xff0c;支持图形界面与命令行双模式操作&#xff0c;在原有功能基础上新增多项实用功能&#xff0c…

作者头像 李华
网站建设 2026/5/29 0:43:52

去除前导 0 的经典代码

【算法分析】 ● 前导 0&#xff08;Leading Zero&#xff09;指的是出现在数字或字符串开头、且在第一个非 0 数字之前的所有 0。 例如&#xff0c;"00123" 的前导 0 是开头的两个 0&#xff0c;去除后应为 "123"&#xff1b;"000" 没有非 0 数…

作者头像 李华
网站建设 2026/5/28 20:34:45

主流小程序商城软件功能架构与服务模式对比分析

因移动互联网不断深入发展&#xff0c;小程序商城已成为众多企业开展数字化经营、创建私域流量的核心工具之一&#xff0c;它具有不用下载安装、即用即走的特性&#xff0c;并且借助微信等超级应用的海量用户基础&#xff0c;在获客成本、用户体验以及运营灵活性方面呈现出明显…

作者头像 李华