Qt开发思想探幽]QObject、模板继承和多继承
@
[Qt开发探幽]QObject、模板继承和多继承
当我们在用Qt开发一个软件框架的时候,在一个正式一点的库或者框架中,我们不可避免地想要使用继承,但是可能当我们开发完一个模块后,会发现一些问题,比如说在编译的时候发现父类会编译不通过。
先说结论:
1.Qt的QObject不支持模板继承。
2.如果需要使用QObject进行多继承的话,子对象引用的父类链至多只能含有一个QObject
3.如果使用模板类和QObject做多继承,依然会编译不通过
1. QObject为什么不允许模板继承:
元对象系统的复杂性:元对象系统(Meta-Object System)是 Qt 的一个重要特性,它允许在运行时获取对象的元信息,如类名、属性、信号、槽等。这个系统需要在编译阶段生成额外的元信息,并且在运行时维护一个元对象表。这个系统在处理模板类时会变得复杂,因为模板类的实例化和使用涉及到编译期和运行时的多个步骤,而元对象系统的设计主要针对静态类型的类层次结构。
编译期类型信息:元对象系统需要在编译期生成一些额外的类型信息,用于支持信号槽和反射等功能。模板类的实例化和类型信息在编译期可能变得模糊不清,这会导致元对象系统难以正确处理模板类的元信息。
类型擦除:C++ 模板的一个特点是类型擦除,即编译器在模板实例化时会生成不同的代码,但生成的代码是针对特定的类型的,而不是模板本身。这种类型擦除使得在运行时获取模板类型信息变得复杂,而元对象系统需要在运行时获取类型信息。
信号槽连接的动态性:信号槽机制允许在运行时动态地连接和断开信号槽。这就要求在运行时能够准确地识别信号和槽的参数类型,以便进行参数匹配。模板类的参数类型在编译期可能不确定,这给信号槽的动态连接带来挑战。
所以在继承了QObject的模板类中,编译会导致QMetaObject找不到元对象而发生编译期报错,所以请不要让任何模板类继承QObject。
你可能会问了,那类似QList和QSharedPointer类为什么可以是模板类,而且也是Qt的东西呢?这恰好是Qt这个库取巧的地方:所有的模板类都是容器,也就是说他们这个模板类中允许装入任何它们想要的东西。或者换句话说,Qt中所有的模板类,都不是QObject的子类,不然的话可以直接看头文件:
也就是说,Qt内部其实自己也是遵循这个规则的:请不要让任何模板类继承QObject。
2.如果需要使用QObject进行多继承的话,子对象引用的父类链至多只能含有一个QObject
QObject有一个很重要的特点,就是不支持拷贝。
在 Qt 的多继承体系中,只有一个类可以拥有 QObject 功能,这个类必须是多继承链中的第一个类。QObject 类为了支持元对象系统、信号槽机制以及其他与运行时类型信息相关的功能,要求继承 QObject 的类在构造函数中通过传递 parent 参数来指定父对象。这个父对象就是用于建立对象之间关系的,例如在父对象析构时,它的子对象也会被析构。
由于 C++ 的多继承特性,当一个类需要继承多个类,而其中一个类是 QObject,为了保证 QObject 的正确功能,必须将 QObject 继承链放在多继承链的第一个位置。这是因为 QObject 继承链在构造和析构过程中有一些特殊的操作,需要在第一个类中执行。
3.如果使用模板类和QObject做多继承,编译不通过
这是因为 QObject 类需要在构造和析构过程中执行特殊的操作,以支持元对象系统、信号槽机制和其他与运行时类型信息相关的功能。而模板类的继承可能会干扰这些特殊操作,导致编译错误或者运行时问题。
QObject 的特殊操作需要在构造函数中执行,并且这些操作需要基于类的实际类型来进行。而模板类在编译时才会实例化,因此编译器在处理模板类时无法获得完整的类型信息,从而无法保证 QObject 的特殊操作能够正确地执行。
为了避免这些问题,Qt 推荐将 QObject 放在继承链的第一个位置,以确保 QObject 的特殊操作可以正确地执行。如果您的类需要多继承,可以考虑使用接口继承(纯虚函数)来实现所需的功能,以避免在多继承中使用 QObject 和模板类导致的问题。
问题场景
为什么我突然会想到这个问题呢,因为我确实遇到了,场景大概是这样的:
我现在在开发一个软件框架。我现在这个框架内的所有对象都需要包含一些信号和槽(这是为了统一上下信息的交换),我们管这个最底层的东西叫TSG_Caller,理论上我这个框架内所有的东西都应该要继承到这个TSG_Caller中,方便我进行管理:

它实际上只有一些信号和槽,我希望任何对象都能通过注册,在kernel内统一地操作这些信号与槽,不仅仅是一些细分的类,大类也要提供这些东西。
当我开发到一定程度之后,我细化到对某个设备的操控。于是我写了一个模板类,用来初始化一些模板类的基本类型。我希望不同的设备有不同的输入参数类型,于是我们就有了如下的设备模板类:

于是这里就出现问题了:QObject和模板类的继承兼容不能说不好,只能说基本完全不允许使用。当然了,这也是可以理解的,毕竟MetaObject这么强大的功能不能支持模板也是理所当然的事。
至于解决方法,那就只能改造这个设备类了,不能用模板类了,而是只能采用通用的输入类型。我这里因为输入的参数类型有限而且都是明文,所以我这里将所有的输入和输出都改成QString传输json字符串,当然了输入的字符串也要提供给外部一个判断的函数,以免外部输入错误。
修改后的接口如下:

不知道有没有更好的办法,也许有,但是目前我只想到这种比较简单的方法
Qt开发思想探幽]QObject、模板继承和多继承的更多相关文章
- C++ 函数模板与类模板(使用 Qt 开发编译环境)
注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言. 模板是 C++ 支持参数化程序设计的工具,通过它可以实现参数多态性.所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一 ...
- Qt开发Active控件:如何使用ActiveQt Server开发大型软件的主框架(2)
Qt开发Active控件:如何使用ActiveQt Server开发大型软件的主框架 注:本文更多地是带着如何去思考答案,而不是纯粹的放一个答案上来,如果你需要直接看到完整的答案,请直接看实例和最后的 ...
- Qt开发Activex笔记(二):Qt调用Qt开发的Activex控件
若该文为原创文章,转载请注明原文出处本文章博客地址:https://blog.csdn.net/qq21497936/article/details/113789693 长期持续带来更多项目与技术分享 ...
- Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解
前话 Qt的图形视图框架,最核心的三个类为:QGraphicsScene.QGraphicsItem与QGraphicsView. 基于图形框架的高级白板软件Demo QGraphicsSce ...
- 基于QT开发的第三方库
基于Qt开发的第三方库 分类: Qt2014-02-12 11:34 1738人阅读 评论(0) 收藏 举报 QT第三方库 目录(?)[+] 文章来源:http://blog.csdn.net ...
- ASP.NET自定义控件组件开发 第五章 模板控件开发
原文:ASP.NET自定义控件组件开发 第五章 模板控件开发 第五章 模板控件开发 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接 ...
- 使用Qt开发绘制多个设备的流量曲线图(附带项目图)
一.说明: 在实际项目中,主要是使用Qt开发CS程序,当然主要是客户端.公司项目中有这个需求是实时显示多个设备的流量曲线图,设备将流量信息发给服务端,服务端再将信息通过Socket发给Qt客户端,Qt ...
- 【Qt开发】QThread 实用技巧、误区----但文档中没有提到
本文主要内容: 在任务一中,用 四 种方式实现:点击界面按钮,开线程运行一段程序,结果显示在一个Label上.1. 用不正确的方式得到看似正确的结果2. 用Qt Manual 和 例子中使用的方法3. ...
- 使用cocoa捕获dock栏中的“退出”事件,解决qt开发的应用程序退出异常的问题
最近在移植一个QT开发的应用程序到mac平台,由于我们的应用在退出时需要释放一些资源,不然在mac系统会报崩溃事件,但是当用户使用dock栏上面的退出功能时,没有捕获到这个退出事件,导致无法正常退出. ...
- 基于arm的嵌入式QT开发(课程设计)
一. 项目要求 配置QT5.7基于x86及arm 等两种CPU架构的调试及开发环境: 移植arm编译后的QT5.7及屏幕校准工具tslib1.4至CORTEX ARM9实验平台: 开发基于QT5.7的 ...
随机推荐
- values() 字典形式显示查询结果
values() 字典形式显示查询结果 name,age为数据库的两个列 Student.objects.values('name','age')
- 基于Sentinel自研组件的系统限流、降级、负载保护最佳实践探索
作者:京东物流 杨建民 一.Sentinel简介 Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. Sentinel 具有以下特征: 丰富的应用场景:秒 ...
- C盘清理,移动node 依赖和缓存文件
由于先前安装的node 没有做任何配置,都是傻瓜式下一步,导致了我很多依赖都放置C盘,内存占用过多:也不太好管理所有觉得将它移动到node安装目录 一.新建文件夹 在原本安装的nodejs目录下新建 ...
- web自动化10-窗口截图、验证码处理
窗口截图 1.是什么 说明:把当前操作的页面,截图保存到指定位置 2.代码中怎么使用? 说明:在Selenium中,提供了截图方法,我们只需要调用即可 方法: driver.get_screensho ...
- 代码随想录算法训练营Day23 二叉树
代码随想录算法训练营 代码随想录算法训练营Day23 二叉树|669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇 669. 修剪二叉搜索树 题目链接 ...
- 美女同事的烦恼:如何配置 Apache SkyWalking 告警?
小婉 技术部基本上是一个和尚庙,女生非常少,即使有女生也略微有点抽象,小婉就不一样,她气质绝佳. 上午,同事小婉刚才从老板办公室里出来,看上去一脸不悦的样子.为了表示对同事的关(ba)心(gua),我 ...
- Go 开源库运行时依赖注入框架 Dependency injection
Dependency injection 一个Go编程语言的运行依赖注入库.依赖注入是更广泛的控制反转技术的一种形式.它用于增加程序的模块化并使其具有可扩展性. 实例展示(High API): typ ...
- 【神经网络】基于GAN的生成对抗网络
目录 [神经网络]基于GAN的生成对抗网络 随着深度学习的快速发展,神经网络逐渐成为人工智能领域的热点话题.神经网络是一种模仿人脑计算方式的算法,其通过大量数据和复杂的计算模型,能够实现复杂的任务和预 ...
- C#使用企业微信群机器人推送生产数据
在日常的工作生产中,经常会有将将生产数据或者一些信息主动推送给相关的管理人员,我们公司在开发WMS系统时,为了仓库的储存安全,需要在危废品库存达到一定的储量时,自动通知仓管员去处理危废品,所以就需要程 ...
- Net 编译器平台 --- Roslyn
引言 最近做一个功能想要动态执行C#脚本,就是预先写好代码片段,在程序运行时去执行代码段,比如像这样(以下代码为伪代码): string scriptText = "int a = 1;in ...