深入研究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.
这个对象说一千道一万,三个核心
The QObject class provides a base class for objects that can take advantage of the meta-object system.
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.
The Meta-Object Compiler (
moc) supplies each QObject subclass with the necessary code to implement meta-object features.
也就是说:
QObject这个类提供了整个元对象系统的一个根基
Q_Object宏这是让一个类可以使用RTTI,信号与槽机制(这就是为什么一些奇奇怪怪的Undefined Reference可以依赖这个解决,下一次发现使用信号与槽机制的时候编译炸了排查的时候考虑这个事情)
Moc则是更加进一步的提供了元对象系统的实现的保证(嘿!想一下你编译的时候是不是需要有moc文件,他就是Meta-object Compilers,元系统编译器产生的)
换而言之,Qt的元对象并不完全直接依赖于语言,而是借助了外来的Moc Tools预先扫描源文件,生成自己的元对象文件,在最后纳入编译阶段合并进来
当然,我们的元对象系统还可以做更多的事情:
QObject::metaObject作为一个静态方法返回关联的metaObject(也就是返回当前对象的元对象系统的那部分)
QMetaObject::className可以进一步返回运行时的对象名称,而这个是基于标准实现而不是编译器实现的,你知道的,一致性!
QObject::inherits则是检查一个类是不是位于Qt的继承树上
QObject::tr则是保证了我们的对象名称满足国际化
QObject::setProperty和QObject::property让我们的对象拥有了属性这个概念!
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编译器的使用,以及还有属性系统,挖个坑,有空讲。
相关文章:
深入研究Qt Meta - Object System
目录 先说RTTI 再说QMeta Object System 关于Q_OBJECT 这篇文章我打算研究一下QMetaObject System,也就是Qt自己构建起来的元对象系统。 先说RTTI 啥是RTTI?这是C编程里的一个常见术语,全称是:运行阶段类型识别(Ru…...
web学习笔记(五十八)
目录 1. v-model 双向数据绑定 2. 事件修饰符 3. 路径别名 4. setup语法糖 4.1 语法糖的概念 4.2 setup语法糖 5. 配置代理服务器 1. v-model 双向数据绑定 v-model 双向数据绑定只能使用在表单标签; v-model双向数据绑定原理:采用 Object.de…...
精准安全运维,统信UOS服务器版V20(1070)漏洞修复指南丨年度更新
随着信息安全威胁的不断升级,操作系统的安全性已成为企业运维的关键要素。 为了确保业务运行环境的安全无忧,统信软件持续致力于技术创新和优化,并于日前重磅推出了统信UOS服务器版V20(1070)。该系统提供了高频补丁更…...
Vue3实战笔记(46)—Vue 3高效开发定制化Dashboard的权威手册
文章目录 前言Dashboard开发总结 前言 后台管理系统中的Dashboard是一种图形化的信息显示工具,通常用于提供一个特定领域或系统的概况。它可以帮助用户监控和分析数据,快速获取重要信息。可以帮助用户监控业务状况、分析数据、获取关键信息和管理资源。…...
MySQL为什么会选错索引
有的时候,我们加了索引,也不一定最终查询语句就能用上索引,因为Innodb要不要使用索引,该使用哪个索引是优化器决定的,它是根据成本(代价)预估来选择的,他会倾向于选择一个成本最低的…...
kafka调优参考建议 —— 筑梦之路
这里主要是从不同使用场景来调优,仅供参考。 吞吐量优先 吞吐量优先使用场景如采集日志。 1. broker配置调优 num.partitions:分区个数,设置为与消费者的线程数基本相等 2. producer配置调优 batch.size 批量提交消息的字节数,…...
Redis(十三) 事务
文章目录 前言事务的特性Redis事务的执行原理Redis中使用事务WATCH UNWATCH实现乐观锁 前言 前面我们学习 MySQL 的时候,肯定也学习了事务。事务是什么?给大家举个例子:假如我给朋友微信转账,我给他转了 100 块钱,当我…...
RK 11.0 多屏模式下修改鼠标进入方式
要求:主屏在左,副屏在右。这种排列情况下鼠标仅可通过主屏的最右侧移入副屏的最左侧,或从副屏的最左侧移入主屏最右侧。 1.RK默认设计 1.1 RK的代码设计是当sys.mouse.presentation1时,鼠标在屏幕边缘的时候就会移入另一个屏幕 …...
【收录 Hello 算法】10.4 哈希优化策略
目录 10.4 哈希优化策略 10.4.1 线性查找:以时间换空间 10.4.2 哈希查找:以空间换时间 10.4 哈希优化策略 在算法题中,我们常通过将线性查找替换为哈希查找来降低算法的时间复杂度。我们借助一个算法题来加深理解。 Question 给…...
浅析部署架构中的GZone、RZone和CZone
在现代软件开发中,理解和应用各种技术概念是成功的重要因素。本文将详细介绍GZone、RZone和CZone三个概念,解释它们的定义、特点、功能及应用场景,并通过实际案例帮助读者更好地理解这些概念。 一、GZone 1.1 定义 GZone是指“Global Zone…...
【全开源】分类记账小程序系统源码(ThinkPHP+FastAdmin+UniApp)
基于ThinkPHPFastAdminUniAppvk-uView-uiVue3.0开发的一款支持多人协作的记账本小程序,可用于家庭,团队,组织以及个人的日常收支情况记录,支持周月年度统计。 :智能管理您的财务生活 一、引言:财务智能化…...
Android NDK系列(四)NDK的编译
Native工程一般会用到NDK,一般开发者使用的NDK是官方提供的,直接下载即可使用。在工作过程中一般很少要定义NDK,不过对于想了解NDK是怎么生成的,可以继续往下阅读。 Google提供了编译NDK的说明文档,地址为NDK编译&…...
Jenkins--从入门到入土
Jenkins–从入门到入土 文章目录 Jenkins--从入门到入土〇、概念提要--什么是CI/DI?1、CI(Continuous Integration,持续集成)2、DI(DevOps Integration,DevOps 集成)3、解决的问题 一、Jenkins安…...
文心一言 VS 讯飞星火 VS chatgpt (267)-- 算法导论20.2 2题
二、写出 PROTO-vEB-DELETE 的伪代码。通过扫描簇内的相关位,来更新相应的 summary 位。并且你实现的伪代码的最坏情况运行时间是多少?如果要写代码,请用go语言。 文心一言: 首先,让我们回顾一下vEB(Van …...
C 语言设计模式(结构型)
文章目录 代理模式场景示例 门面模式场景示例 桥接模式场景示例 适配器模式场景示例 外观模式场景示例 享元模式场景示例 装饰器模式场景示例 组合模式场景示例 代理模式 C语言中,代理模式通常用于实现对象的间接访问。代理模式是一种结构型设计模式,它…...
【云原生--K8S】K8S python接口研究
文章目录 前言一、搭建ubuntu运行环境1.运行ubuntu容器2.拷贝kubeconfig文件二、python程序获取k8s信息1.获取node信息2.获取svc信息3.常用kubernetes API总结前言 在前面的文章中我们都是通过kubectl命令行来访问操作K8S,但是在实际应用中可能需要提供更方便操作的图形化界面…...
5.26作业
服务器 2 3 #define BUFSIZE 10244 #define login_msg_len 205 6 typedef struct Node{7 char name[login_msg_len];8 struct sockaddr_in addr;9 struct Node *next;10 }Node;11 12 typedef struct Msgtype{13 char type;14 char username[login_msg_len]…...
链接库文件体积优化工具篇:bloaty
笔者之前参与过一个嵌入式智能手表项目,曾经碰到过这样一个问题:手表的flash大小只有2M,这意味着只能在上面烧录2M大小的代码。随着开发不断进行,代码越写越多,编译出来的bin也越来越大。最后bin大小超过了2M, 就没法烧…...
使用pyqt绘制一个爱心!
使用pyqt绘制一个爱心! 介绍效果代码 介绍 使用pyqt绘制一个爱心! 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QPainter, QPen, QBrush, QColor from PyQt5.QtCore import Qt, Q…...
关于 Transformer 的11个常见面试题
Transformer 是如何工作的? Transformer 是一种深度学习算法,特别适用于自然语言处理(NLP)任务,如语言翻译、语言生成和语言理解。它们能够处理长度可变的输入序列并捕捉长距离依赖关系,使其在理解和处理自…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
Java设计模式:责任链模式
一、什么是责任链模式? 责任链模式(Chain of Responsibility Pattern) 是一种 行为型设计模式,它通过将请求沿着一条处理链传递,直到某个对象处理它为止。这种模式的核心思想是 解耦请求的发送者和接收者,…...
