理解继承与组合的本质:Qt 项目中的设计选择指南
文章目录
- 理解继承与组合的本质:Qt 项目中的设计选择指南
- 一、继承与组合的本质区别
- 1. 继承(Inheritance)
- 2. 组合(Composition)
- 二、继承的适用场景
- 三、组合的适用场景
- 四、错误使用继承的后果
- 五、判断继承或组合的三问法
- 六、继承与组合的对比总结
- 七、结语
理解继承与组合的本质:Qt 项目中的设计选择指南
在使用 Qt 进行 C++ 项目开发时,开发者常常会遇到这样的困惑:当一个类需要访问另一个类的某个控件或功能时,应该选择继承还是组合?尤其在项目中,我们常常希望在辅助模块中操作主窗口中的 QMdiArea
,这时到底是继承主窗口类还是将 QMdiArea*
传递过去?
这一选择关系到项目结构是否合理、代码是否易于扩展、是否易于维护。因此,理解继承与组合的本质,并在实际项目中做出正确选择,至关重要。
一、继承与组合的本质区别
1. 继承(Inheritance)
继承用于表达“是一个(is-a)”的关系。当子类本身就是父类的一个特化版本,继承是合理且必要的。例如:
- “学生”是一个“人”
- “编辑器”是一个“文本控件”
- “自定义主窗口”是一个“QMainWindow”
在这些场景下,子类继承父类,可以复用父类的功能,并通过重写父类的函数来扩展或修改行为。
2. 组合(Composition)
组合用于表达“有一个(has-a)”或“使用一个(uses-a)”的关系。当一个类只是用到了另一个类的某个成员或功能,而不是其本身的子类型,组合是更合适的选择。例如:
- “电脑”有一个“CPU”
- “主窗口”拥有一个“QMdiArea”
- “文件打开类”使用了“主窗口的 QMdiArea 来添加子窗口”
组合通过将对象作为成员或传参传入,实现灵活的对象协作,同时保持各个模块的低耦合。
二、继承的适用场景
以下是继承适合使用的具体场景:
- 需要复用父类的大量行为和结构。例如自定义控件时,你可能想要继承
QTextEdit
来扩展其行为,如实现特定快捷键、右键菜单等。 - 需要重写父类的虚函数或事件处理函数。如
paintEvent()
、mousePressEvent()
等,只有继承父类才能重写这些成员函数。 - 存在明确的类型从属关系。如果语义上“某类是另一类的子类”,那么使用继承是自然且符合逻辑的。
- 实现多态接口。如果某个类需要作为基类供多个子类实现多态接口,如 Qt 的插件框架,这时继承是必需的。
示例:
class MyEditor : public QTextEdit {// “MyEditor 是一个 QTextEdit”void contextMenuEvent(QContextMenuEvent *event) override {// 重写默认右键菜单}
};
三、组合的适用场景
组合更适合以下情况:
- 只需要使用另一个类的部分功能,而不需要继承其全部接口。这有助于减少不必要的耦合。
- 不同模块之间无“类型层级”关系,仅是功能依赖关系。如主窗口与文件处理模块之间的关系。
- 希望将某些功能抽离成可复用的独立类,方便其他模块调用。
- 在多个对象中使用相同的成员组件,且需要灵活替换或复用。
示例:
class FileOpen : public QObject {
public:explicit FileOpen(QMdiArea* mdiArea, QObject* parent = nullptr): QObject(parent), m_mdiArea(mdiArea) {}void openDocument() {QTextEdit* editor = new QTextEdit;m_mdiArea->addSubWindow(editor);editor->show();}private:QMdiArea* m_mdiArea;
};
在这个例子中,FileOpen
并不是主窗口,也不需要成为窗口的一部分。它只是使用了主窗口中的 QMdiArea
,因此组合是最合适的方式。
四、错误使用继承的后果
在实际项目中,有些开发者为了访问主窗口中的控件而选择继承主窗口类,例如:
class FileOpen : public Vs_Wps {// 继承只是为了访问 ui->mdiArea
};
这种做法存在以下严重问题:
- 违反语义:
FileOpen
并不是主窗口的一种类型,不应该“是一个主窗口”。 - 强耦合:
FileOpen
完全绑定在Vs_Wps
上,无法在其他窗口中复用。 - 资源冗余:继承主窗口会默认加载所有界面资源,造成浪费。
- 维护困难:当主窗口结构调整时,
FileOpen
会受到不必要的影响。
正确的方式应该是通过构造函数或 setter 方法将 QMdiArea*
传递给 FileOpen
,由其独立管理功能逻辑。
五、判断继承或组合的三问法
- A 是 B 吗?(是 → 继承;不是 → 组合)
- 我是否要复用 B 的大部分功能?(是 → 继承)
- 我是否只用到 B 的一个成员或功能?(是 → 组合)
这三个问题可以快速判断设计方向是否正确。
六、继承与组合的对比总结
对比项 | 继承 | 组合 |
---|---|---|
语义关系 | is-a(是一个) | has-a / uses-a(有一个/用一个) |
耦合性 | 高 | 低 |
灵活性 | 差 | 高 |
可复用性 | 差 | 好 |
可测试性 | 差 | 好 |
代码维护 | 难(改父类影响子类) | 易(只影响局部) |
Qt 中典型场景 | 自定义控件、主窗口 | 模块功能类、逻辑分离类 |
七、结语
继承与组合是面向对象设计中的基础,也是工程结构清晰与否的关键。错误的继承不仅会带来维护困难,还可能造成逻辑混乱和功能耦合。Qt 项目开发中,尤其推荐优先使用组合的方式,让各个模块职责清晰、协作明确。
当我们从“我是否是它”转变为“我是否用它”的思维时,往往就已经走在了正确设计的道路上。理解设计哲学,比会写代码更重要。
相关文章:
理解继承与组合的本质:Qt 项目中的设计选择指南
文章目录 理解继承与组合的本质:Qt 项目中的设计选择指南一、继承与组合的本质区别1. 继承(Inheritance)2. 组合(Composition) 二、继承的适用场景三、组合的适用场景四、错误使用继承的后果五、判断继承或组合的三问法…...

新手小白使用VMware创建虚拟机安装Linux
新手小白想要练习linux,找不到合适的地方,可以先创建一个虚拟机,在自己创建的虚拟机里面进行练习,接下来我给大家接受一下创建虚拟机的步骤。 VMware选择创建新的虚拟机 选择自定义 硬件兼容性选择第一个,不同的版本&a…...
使用 PHP 和 Guzzle 对接印度股票数据源API
对接 StockTV API 可能涉及获取实时或历史的金融市场数据,如股票价格、交易量、市场新闻等。为了帮助你更好地理解如何使用 PHP 对接 StockTV API,下面我将提供一个通用指南和示例代码。 前提条件 注册并获取API密钥:首先你需要在 StockTV …...

EscapeX:去中心化游戏,开启极限娱乐新体验
VEX 平台推出全新去中心化游戏 EscapeX(数字逃脫),创新性地将大逃杀玩法与区块链技术相融合。用户不仅能畅享紧张刺激的解谜过程,更能在去中心化、公正透明的环境中参与游戏。EscapeX 的上线,为 VEX 生态注入全新活力&…...

使用PyQt5的图形用户界面(GUI)开发教程
文章目录 写在前面一、PyQt5的安装1.1 使用Conda管理环境1.1.1 新建环境1.1.2 conda list和pip list的区别1.1.3 conda install和pip install的区别 1.2 安装PyQt5和Qt Designer1.3 VsCode中配置Qt Designer 二、PyQt5的UI设计2.1 .ui文件设计2.2 .qrc文件建立2.3 qss设计 三、…...
STM32实战:智能环境监测站设计方案
下面是一个基于STM32的智能环境监测站设计方案,使用Keil MDK-ARM开发环境。这个系统集成了多种传感器,并通过OLED显示数据,同时具备数据存储和报警功能。 [STM32F4系列MCU] ├── I2C总线 │ ├── SHT30温湿度传感器 │ ├──…...
猎板硬金镀层厚度:新能源汽车高压系统的可靠性基石
在新能源汽车的电池管理系统(BMS)和电机控制器中,硬金镀层厚度直接关系到高压环境下的电气稳定性与使用寿命。猎板针对车载场景开发的耐电迁移方案(金层 2.5μm,镍层 8μm),经 150℃/85% RH 高压…...
KEYSIGHT是德科技 E5063A 18G ENA系列网络分析仪
KEYSIGHT是德科技 E5063A 18G ENA系列网络分析仪 E5063A ENA 矢量网络分析仪 18GHz 2端口 降低无源射频元器件的测试成本 Keysight E5063A ENA 是一款经济适用的台式矢量网络分析仪,可用于测试简单的无源元器件,例如频率最高达到 18 GHz 的天线、滤…...
VR 虚拟仿真工器具:开启医学新视界的智慧钥匙
VR 虚拟仿真工器具在医疗领域的应用,为医疗行业的发展带来了新的机遇。在手术模拟训练中,它让医生提前熟悉手术流程和操作技巧。对于一些复杂的手术,如心脏搭桥手术、神经外科手术等,手术难度大、风险高,对医生的操作技…...
webshell管理工具、C2远控服务器流量分析
文章目录 一、Webshell管理工具流量分析1. 蚁剑(AntSword)2. 冰蝎(Behinder)3. 哥斯拉(Godzilla)二、常见C2远控服务器流量分析1. Metasploit2. CobaltStrike 三、防御对抗策略总结 一、Webshell管理工具流…...

JavaWeb:前端工程化-TS(TypeScript)
概述 快速入门 常用类型 基础类型 联合类型 函数类型 对象类型 接口Interface Interface和type区别 典型推论...

unity+ spine切换武器不换皮肤解决方案
1.在spine编辑中获取到角色武器插槽名称 这里的武器插槽名称为“zj_22”。角色的spine正常导出到unity中。 2.将需要替换的武器图片单独放在一个spine项目里面,并为每个武器单独建立一个插槽。 而且全部放在根骨骼Root下。 3.将武器的spine动画导出,会…...

[java八股文][MySQL面试篇]SQL基础
NOSQL和SQL的区别? SQL数据库,指关系型数据库 - 主要代表:SQL Server,Oracle,MySQL(开源),PostgreSQL(开源)。 关系型数据库存储结构化数据。这些数据逻辑上以行列二维表的形式存在,每一列代表…...
Ubuntu中SSH服务器安装使用
SSH服务安装 1. 安装 OpenSSH 安装 SSH 服务端(允许远程登录) sudo apt update sudo apt install openssh-server安装 SSH 客户端(用于连接其他服务器) sudo apt install openssh-client2. 检查 SSH 服务状态 sudo systemctl…...

【AI论文】SWE-rebench:一个用于软件工程代理的任务收集和净化评估的自动化管道
摘要:基于LLM的代理在越来越多的软件工程(SWE)任务中显示出有前景的能力。 然而,推进这一领域面临着两个关键挑战。 首先,高质量的训练数据稀缺,尤其是反映现实世界软件工程场景的数据,在这些场…...

Flask文件处理全攻略:安全上传下载与异常处理实战
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...

【算法深练】分组循环:“分”出条理,化繁为简
目录 引言 分组循环 2760. 最长奇偶子数组 1446. 连续字符 1869. 哪种连续子字符串更长 2414. 最长的字母序连续子字符串的长度 3456. 找出长度为 K 的特殊子字符串 1957. 删除字符使字符串变好 674. 最长连续递增序列 978. 最长湍流子数组 2110. 股票平滑下跌阶段的…...

焊缝缺陷焊接缺陷识别分割数据集labelme格式5543张4类别
数据集中有超过一半为增强图片,请认真观察图片预览 数据集格式:labelme格式(不包含mask文件,仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数):5543 标注数量(json文件个数):5543 标注类别数:4…...

关于scrapy在pycharm中run可以运行,但是debug不行的问题
关于scrapy在pycharm中run模式可以运行,但是debug模式不行的问题 文章目录 关于scrapy在pycharm中run模式可以运行,但是debug模式不行的问题查了下原因 点击run就可以运行,但是debug就是运行不了 一点击debug就报这个错,也不知道啥…...

Java高级 | 【实验四】Springboot 获取前端数据与返回Json数据
隶属文章: Java高级 | (二十二)Java常用类库-CSDN博客 系列文章: Java高级 | 【实验一】Spring Boot安装及测试 最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot …...
云数据库选型指南:关系型 vs NoSQL vs NewSQL的企业决策
在云时代,数据库选型直接关系到企业应用性能和成本效益。本文深入分析三大数据库类型,助您做出明智决策。 目录概览 关系型数据库:经典之选NoSQL数据库:灵活应对非结构化数据NewSQL数据库:融合的优势三大数据库对比分…...

Prj08--8088单板机C语言8255读取按键码
1.验证结果 2.代码片 key_codeinp(PORT_8255_C)&0x0f;tiny_sprintf(buffer,"Key_code 0X%x \r\n",key_code);uart_str_send(buffer); 3.完整代码 #include "tiny_stdarg.h" // 使用自定义可变参数实现#define ADR_273 0x0200 #define ADR_244 0x…...

蜜獾算法(HBA,Honey Badger Algorithm)
2021年由Hashim等人提出(论文:Honey Badger Algorithm: A New Metaheuristic Algorithm for Solving Optimization Problems)。模拟蜜獾在自然界中的智能捕食行为,属于群体智能优化算法(与粒子群PSO、遗传算法GA同属一…...

Modbus转Ethernet IP网关助力罗克韦尔PLC数据交互
在工业自动化领域,Modbus协议是一种广泛应用的串行通信协议,它定义了主站和从站之间的通信规则和数据格式。罗克韦尔PLC是一种可编程的逻辑控制器,通过Modbus协议实现与其他设备之间的数据交互。然而,随着以太网技术的普及和发展&…...

飞算JavaAI 炫技赛重磅回归!用智能编码攻克老项目重构难题
深夜还在排查十年前Hibernate框架埋下的N1查询隐患?跨语言迁移时发现SpringMVC控制器里的业务逻辑像一团乱麻?当企业数字化进入深水区,百万行代码的老系统就像一座随时可能崩塌的"技术债冰山"。近日,飞算科技发布JavaAI…...
青少年编程与数学 02-020 C#程序设计基础 15课题、异常处理
青少年编程与数学 02-020 C#程序设计基础 15课题、异常处理 一、异常1. 异常的分类2. 异常的作用小结 二、异常处理1. 异常处理的定义2. 异常处理的主要组成部分3. 异常处理的作用小结 三、C#异常处理1. 异常的基本概念2. 异常处理的关键字3. 异常处理的流程4. 自定义异常5. 异…...
Electron打包前端和后端为exe
文章目录 什么是Electron? 安装electron过程 其他git项目地址比较好的文章electron的替代品安装报错 npm ERR! request to https://registry.npm.taobao.org/electron failed, reason: certificate has expired安装提示 npm WARN deprecated boolean3.2.0: Package …...
unix/linux,sudo,一个强大且灵活的工具,允许一个被授权的用户以另一个用户(通常是root,即超级用户)的身份来执行命令
sudo:不仅仅是“用管理员权限运行” sudo 这个词,来源于 SuperUser DO (或者 Substitute User DO,后者的含义更为广阔和准确)。它是一个强大且灵活的工具,允许一个被授权的用户以另一个用户(通常是root,即超级用户)的身份来执行命令。 1. Unix/Linux 的权限哲学:最小…...
JavaScript 二维数组初始化:为什么 fill([]) 是个大坑?
JavaScript 二维数组初始化:为什么 fill([]) 是个大坑? 今天刷leetcode的时候,遇到一个神奇的bug。 当我修改数组中的一个元素,却意外影响了其他所有元素???。 问题重现:诡异的数组…...
项目任务,修改svip用户的存储空间。
修改存储空间 3GB->5GB,这是项目任务,首先有人任务就要去思考实现思路,首先存储空间,也就是说不只是前端样式3GB改一下就可以了,那用户实际还是3GB,所以我们去网站看后端谁返回给我们了3GB,我…...