深入研究Qt Meta - Object System

目录

先说RTTI

再说QMeta Object System

关于Q_OBJECT


这篇文章我打算研究一下QMetaObject System,也就是Qt自己构建起来的元对象系统。

先说RTTI

啥是RTTI?这是C++编程里的一个常见术语,全称是:运行阶段类型识别(Runtime Type Identification),关于RTTI如何在原生C++中使用不是我们这里的重点,但是可以明确的一点是——跟编译器实现密切相关,意味着可移植性略差。很多类库已经为其类对象提供了实现这种功能的方式,但由于C++内部并不支持,因此各个厂商的机制通常互不兼容

即使编译器支持RTTI,就目前而言,原生的支持仍然十分的不足。我们没有办法完全知道例如类的名字、有哪些父类、有哪些成员变量、有哪些成员函数、哪些是public的、哪些是private的、哪些是protected的等等。

有时候一个工程项目可能包含成千上万个类,完整的保存这些信息将会消耗大量的内存资源。为了节省内存,C++标准约定typeid只能返回类名。因此,仅靠dynamic_cast和typeid两个关键字提供的类型信息实在有限。更何况,他还会造成大量的系统开销,这也是为什么这个特性并没有被完整的纳入标准。

关于RTTI,可以参看:【C++】RTTI有什么用?怎么用? - 知乎 (zhihu.com)以备快速的复习

再说QMeta Object System

下面我们聊聊,既然大家都各做各的,Qt框架作为C++早期时代就存在的框架,自然实现了自己的一套源系统机制。

这个元对象机制不光实现了类似于RTTI那样的动态查看类信息的作用,还扩展出了信号与槽的机制(这个就是大名鼎鼎的信号与槽)

Qt's meta-object system provides the signals and slots mechanism for inter-object communication, run-time type information, and the dynamic property system.

这个对象说一千道一万,三个核心

  1. The QObject class provides a base class for objects that can take advantage of the meta-object system.

  2. The Q_OBJECT macro inside the private section of the class declaration is used to enable meta-object features, such as dynamic properties, signals, and slots.

  3. The Meta-Object Compiler (moc) supplies each QObject subclass with the necessary code to implement meta-object features.

也就是说:

  1. QObject这个类提供了整个元对象系统的一个根基

  2. Q_Object宏这是让一个类可以使用RTTI,信号与槽机制(这就是为什么一些奇奇怪怪的Undefined Reference可以依赖这个解决,下一次发现使用信号与槽机制的时候编译炸了排查的时候考虑这个事情)

  3. Moc则是更加进一步的提供了元对象系统的实现的保证(嘿!想一下你编译的时候是不是需要有moc文件,他就是Meta-object Compilers,元系统编译器产生的)

换而言之,Qt的元对象并不完全直接依赖于语言,而是借助了外来的Moc Tools预先扫描源文件,生成自己的元对象文件,在最后纳入编译阶段合并进来

当然,我们的元对象系统还可以做更多的事情:

  1. QObject::metaObject作为一个静态方法返回关联的metaObject(也就是返回当前对象的元对象系统的那部分)

  2. QMetaObject::className可以进一步返回运行时的对象名称,而这个是基于标准实现而不是编译器实现的,你知道的,一致性!

  3. QObject::inherits则是检查一个类是不是位于Qt的继承树上

  4. QObject::tr则是保证了我们的对象名称满足国际化

  5. QObject::setProperty和QObject::property让我们的对象拥有了属性这个概念!

  6. QMetaObject::newInstance()以一种工厂方法构造了这个类的一个新实例

我们知道dynamic_cast可以用来转化父类子类,而且转化成不成功全看是不是真的如此。这里我们入乡随俗,使用qobject_cast来检查Qt元对象的继承问题。

我随手写一个简单的demo:

#include <QWidget>
#include <QMainWindow>
#include <QApplication>
class MyObject : public QWidget{};
​
​
int main(int argc, char *argv[])
{QApplication app(argc, argv); // Import For QWidgets enableQObject* obj = new MyObject;
​QWidget* widget = qobject_cast<QWidget*>(obj);if(widget){qDebug() << "Is Widget";}
​QMainWindow* window = qobject_cast<QMainWindow*>(obj);if(window){qDebug() << "Is Window";}
​delete obj;
}

值得注意的是,如果我们希望纳入一个类进入QObject的继承对象树中,务!必!在私有区域声明一个Q_OBJECT。(当然要是想要直接暴露给外面的话放在public也不是不行)

手撸了一个例子

#include <QWidget>
#include <QMainWindow>
#include <QApplication>
#define IS_USE_QOBJ_MACRO 0
​
class MyObject : public QWidget{
#if IS_USE_QOBJ_MACROQ_OBJECT
#endif
public:QString _ClassName(){return this->metaObject()->className();}
};
​
​
int main(int argc, char *argv[])
{QApplication app(argc, argv);QObject* obj = new MyObject;
​QWidget* widget = qobject_cast<QWidget*>(obj);if(widget){qDebug() << "Is Widget";}
​QMainWindow* window = qobject_cast<QMainWindow*>(obj);if(window){qDebug() << "Is Window";}
​qDebug() << dynamic_cast<MyObject*>(obj)->_ClassName();
​delete obj;
}
​
#if IS_USE_QOBJ_MACRO
#include "main.moc" // 一个Demo,我们直接自己引入编译好的main.moc
#endif

你可以留意到,添加了QOBJECT宏的类的行为表现的并不一致。

#define IS_USE_QOBJ_MACRO 0
Is Widget
"QWidget"
#define IS_USE_QOBJ_MACRO 1
Is Widget
"MyObject"

由此,如果想要让元对象系统正确的工作,请务必使用Q_OBJECT

关于Q_OBJECT

#define Q_OBJECT \
public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_TR_FUNCTIONS \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal { explicit QPrivateSignal() = default; }; \QT_ANNOTATE_CLASS(qt_qobject, "")

这就是我们的源码。

可以看到他实际上就是向我们的类内嵌入了工作函数。这就是为什么需要添加一些类。

当然还有MOC编译器的使用,以及还有属性系统,挖个坑,有空讲。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/13994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Chrome DevTools攻略

Chrome DevTools&#xff0c;也称为Chrome开发者工具&#xff0c;是一套直接内置于Google Chrome浏览器的Web开发者工具。以下是一些使用Chrome DevTools的攻略和技巧&#xff1a; 打开DevTools&#xff1a; 右键点击页面上的任何元素&#xff0c;选择“检查”或“审查元素”。…

2024年华为OD机试真题-机场航班调度程序-C++-OD统一考试(C卷D卷)

题目描述: XX市机场停放了多架飞机,每架飞机都有自己的航班号CA3385,CZ6678,SC6508等,航班号的前2个大写字母(或数字)代表航空公司的缩写,后面4个数字代表航班信息。但是XX市机场只有一条起飞用跑道,调度人员需要安排目前停留在机场的航班有序起飞。为保障航班的有序起…

【webrtc】MediaEngine的实现CompositeMediaEngine创建VOE

m98音视频的引擎是管理channel的看起来是外部强加给CompositeMediaEngine 管理的。CompositeMediaEngine :合成媒体引擎 G:\CDN\rtcCli\m98\src\media\base\media_engine.h// CompositeMediaEngine constructs a MediaEngine from separate // voice and video engine classes…

Python中文分词工具库之jieba使用详解

概要 在自然语言处理(NLP)领域,中文文本的分词是一个重要且基础的任务。Python的jieba库是一个广泛使用的中文分词工具,提供了丰富的功能,包括精准模式、全模式、搜索引擎模式等,适用于不同的应用场景。本文将详细介绍jieba库,包括其安装方法、主要特性、基本和高级功能…

代码随想录35期Day49-Java

Day49题目 LeetCode123买卖股票三 核心思想:和昨天的买卖股票相比,这个只允许买两次,因此把状态新增几个,可见代码注释 class Solution {public int maxProfit(int[] prices) {// 设置五个状态 0 : 无操作 , 1 : 第一次买入, 2 : 第一次卖出 , 3: 第二次买入, 4:第二次卖出…

java技术:oauth2协议

目录 一、黑马程序员Java进阶教程快速入门Spring Security OAuth2.0认证授权详解 1、oauth服务 WebSecurityConfig TokenConfig AuthorizationServer 改写密码校验逻辑实现类 2、oauth2支持的四种方式&#xff1a; 3、oauth2授权 ResouceServerConfig TokenConfig 4、…

前端面试题日常练-day19 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末。 1. AJAX是什么的缩写&#xff1f; A. Asynchronous JavaScript and XMLB. Asynchronous JavaScript and XHTMLC. Asynchronous Java and XMLD. Asynchronous Java and XHTML2. 下列哪个方法用于创建…

SpringCloudAlibaba 动态读取配置文件的信息

传统读取方式&#xff1a; 在application.properties中写入要读取的内容&#xff0c;如下&#xff1a; coupon.user.nameTom coupon.user.age27 接口引入处&#xff1a; Value("${coupon.user.name}")private String name;Value("${coupon.user.age}")p…

MySQL的索引是什么

MySQL的索引 一、索引概述二、索引结构1.简要概述2.从二叉树说起3.再在说下B-Tree4.为什么选择BTree5.Hash又是什么6.博主被面试官经常问的题目 三、索引分类四、聚集索引&二级索引五、索引语法 一、索引概述 1.索引是帮助MySQL 高效获取数据的数据结构(有序)。在数据之外…

[STM32-HAL库]Flash库-HAL库-复杂数据读写-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C6T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.导入Flash库 3.分析地址范围 4.找到可用的地址 5.写入读取普通数据 6.写入读取字符串 6.1 存储相关信息 6.2 存取多个参数 三、总结及源码 一、前言 在面对需要持久化存储的数据时&#xff0c;除了挂载TF卡&#xff0c;我们…

燃数科技前端25-40K*14薪一面超简单,下周二面啦

一面 1、自我介绍 2、低代码如何设计的 3、react路由原理 4、react生命周期 5、什么是回调地狱&#xff0c;如何解决 6、jwt和session有什么区别 7、js文件相互引用有什么问题&#xff1f;如何解决 8、一个很大的json文件&#xff0c;前端读取如何优化 面试我的不像是…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…

力扣:1306. 跳跃游戏 III

1306. 跳跃游戏 III 这里有一个非负整数数组 arr&#xff0c;你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时&#xff0c;你可以跳到 i arr[i] 或者 i - arr[i]。 请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。 注意&#xff0c;不管是什么情况下…

数据库|基于T-SQL创建数据库

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; SQL Server用于操作数据库的编程语言为Transaction-SQL,简称T-SQL。 本节学习基于T-SQL创建数据库。以下为学习笔记。 01 打开新建查询 首先连接上数据库&#xff0c;点击【新建查询】打开新建查询窗口&#xff0c; …

appium-driver方法待整理。。

app C:\Users\v-hongweishi\AppData\Local\Programs\Xmind\Xmind.exe deviceName DESKTOP-7NJ1ENB platformName Windows 应用程序ID&#xff08;AppId&#xff09;是应用程序用户模型 ID (AppUserModelID)&#xff0c;简称 AUMID Outlook …

Leetcode 113:路径总和II

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 public static List<List<Integer>> pathSum(TreeNode root, int targetSum) {List<List&l…

C++—结构体

结构体&#xff08;struct&#xff09;&#xff0c;是一种用户自定义复合数据类型&#xff0c;可以包含不同类型的不同成员。 结构体的声明定义和使用的基本语法&#xff1a; // 声明结构体struct 结构体类型 { 成员1类型 成员1名称; ...成员N类型 成员N名称; };除声明…

【计算机视觉(2)】

基于Python的OpenCV基础入门——视频的处理 视频OpenCV视频处理操作&#xff1a;创建视频对象判断视频是否成功初始化读取视频帧获取视频特征设置视频参数声明编码器保存视频释放视频对象 视频处理基本操作的代码实现&#xff1a; 视频 视频是由一系列连续的图像帧组成的。每一…

Spring—IoC

目录 1. IoC的提出 2. Spring容器 2.1. Spring容器实现原理 2.2. Spring组件 2.2.1 XML标签方式 2.2.2. 类注解方式 2.2.3. 方法注解方式 2.3. Spring容器分类 2.3.1. BeanFactory容器 2.3.2. ApplicationContext容器 2.3.3. WebApplicationContext容器 3. Spring中…

Srping 历史

一、History of Spring and the Spring Framework Spring came into being in 2003 as a response to the complexity of the early J2EE specifications. While some consider Java EE and its modern-day successor Jakarta EE to be in competition with Spring, they are …