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的 ...
 
随机推荐
- Django-2:创建项目Project
			
命令:django-admin startproject mysite PS C:\Users\liujun> cd e:\pyapp\cmdbPS E:\pyapp\cmdb> djan ...
 - this关键字理解
			
编译器对对象的加载步骤: (1)类名 (2)成员变量 (3)成员方法 即使定义类时,成员变量写在成员方法后面,加载对象时,也是先加载成员变量 当编译器识别方法时,会对成员方法改写,在所有方法里隐藏一个 ...
 - .Net8顶级技术:IR边界检查之IR解析(二)
			
前言 IR技术应用在各个编程语言当中,它属于JIT的核心部分,确实有点点麻烦.但部分基本明了.本篇通过小例子了解下.前情提要,看这一篇之前建议看看前一篇:点击此处,以便于理解. 概括 1.前奏 先上C ...
 - react 代码自动格式化
			
咦写了几行代码发现保存后没有被格式化? import React from "react"; import {Row,Col } from "antd"; ex ...
 - drf——权限、认证源码分析、过滤、排序、分页
			
权限.认证源码(了解) 权限源码 # 继承了APIView才有的--->执行流程--->dispatch中的三大认证 self.initial(request, *args, **kwar ...
 - odoo开发教程六:工作流、安全机制、向导
			
一:工作流 工作流是与业务流程相关联的模型,可用于跟踪工序的动态演变过程. 工作流.活动(节点或操作).转换通常在xml里以record定义.在工作流中处理的单个流程称为工作项. 与模型关联的工作流是 ...
 - .Net8罕见的技术:MSIL的机器码简析
			
前言 一般的只有最终的汇编代码才有机器码表示,然一个偶然的机会发现,MSIL(Microsoft intermediate language)作为一个中间语言表示,居然也有机器码,其实这也难怪,计算机 ...
 - 创建springboot工程失败解决 spring initializr Error:cannot download
			
创建springboot工程失败解决 问题描述 原因分析: 网络不好,因为springBooT项目的创建时必须联网的 解决方案: 方案一: 将创建 springBoot 工程的地址更换为如下的地址 阿 ...
 - 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据
			
[Ooonly新人贴]记录工作中遇到的问题,话不多说先上干货 问题:类似K线与蓝牙接收部门模块,要求由原来的接收串口中断改为DMA接收.据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一 ...
 - Spring Cloud Gateway编码实现任意地址跳转
			
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 作为<Spring Cloud Gat ...