深入研究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)任务,如语言翻译、语言生成和语言理解。它们能够处理长度可变的输入序列并捕捉长距离依赖关系,使其在理解和处理自…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
