QT 中的元对象系统
作为一名十几年的 C++ 程序员,最近一段时间使用 QT 开发程序,发现 QT 中还是有许多值得深入理解的技术。QT 不仅仅是一个应用程序开发框架,还有一些对标准 C++ 的扩充。本文和大家一起探讨 QT 中的元对象系统。
在分析 QT 中的元对象系统之前,我们先回顾一下 C++ 中的 RTTI 机制。
C++的RTTI机制
RTTI 是 Runtime Type Identification 的缩写,字面意思是运行时类型识别。C++引入这个机制是为了让程序在运行时能根据基类的指针或引用来获得该指针或引用所指的对象的实际类型。
为什么需要在运行时根据基类的指针或引用来获得实际所指对象的类型呢?这又牵扯到面向对象编程中的多态。
C++ 中的多态是指通过一个基类指针或引用调用一个虚函数时,会根据具体对象的类型来调用该虚函数的不同实现。这样可以实现对象间的通信和转换,以及多态的行为和表现。举个例子:
#include <iostream>
#include <vector>
using namespace std;class Animal {
public:virtual void speak() = 0; // pure virtual function
};class Cat : public Animal {
public:void speak() override {cout << "Meow" << endl;}
};class Dog : public Animal {
public:void speak() override {cout << "Woof" << endl;}
};int main() {vector<Animal*> animals; // create a vector of Animal pointersanimals.push_back(new Cat()); // push a Cat object to the vectoranimals.push_back(new Dog()); // push a Dog object to the vectorfor (auto a : animals) { // use range-based for loop to iterate the vectora->speak(); // polymorphism, calls Cat::speak or Dog::speak}return 0;
} 在上面的代码中,假设是一个屋子里的动物,调用者不用关心具体是猫还是狗,直接调用共同的接口 speak 即可。
可以看出,多态的好处很明显,可以实现代码的抽象和封装,因为我们可以通过一个基类指针或引用来隐藏对象的具体类型和实现细节,而只暴露对象的公共接口。这在基于插件的系统架构中使用得非常广泛,比如 Visual Studio Code 就是靠插件支撑起来的。多态是一种非常有用的编程技巧,它可以让我们的代码更加复用和扩展,以及更加抽象和封装,提高我们的编程效率和质量。
但有的时候,我们可能需要在运行时,鉴别出目前的对象是猫或者狗,比如狗需要定时出去遛,猫不需要。一种解决方法是在基类 Animal 中定义一个 walk方法,并给一个默认实现:
class Animal {
public:virtual void speak() = 0; // pure virtual functionvirtual void walk() {}
}; 在 Dog 类中重写 walk 方法,而在 Cat 类中直接使用缺省的空实现。但这种方法有个明显的问题,就是会引起类方法的膨胀,随着继承越来越多,会发现不同类之间有差别的方法越来越多,都塞进基类,会使得类臃肿不堪。
这个时候就可以请 RTTI 机制出场了。
C++ 的 RTTI 主要包括两个关键字:typeid 和 dynamic_cast。typeid 运算符,用于返回表达式的类型。dynamic_cast 运算符,用于将基类类型的指针或引用安全地转换为其派生类类型的指针或引用。
typeid 运算符返回一个对 type_info 对象的引用,其中,type_info 是在头文件中定义的一个类,这个类重载了 == 和 != 运算符,以便可以用于对类型进行比较。type_info 类的实现随厂商而异,但包含一个 name() 成员,该函数返回一个随实现而异的字符串,通常(但并非一定)是类的名称。例如,下面的代码可以判断 pg 指向的是否是 ClassName 类的对象:
#include <typeinfo>
...
if (typeid(Dog) == typeid(*a)) {// a points to a Dog object
} dynamic_cast运算符可以用于指针和引用的类型转换,它的语法如下:
dynamic_cast<type>(expression) 其中,type 是目标类型,expression 是要转换的表达式。如果转换成功,dynamic_cast 返回一个指向目标类型的指针或引用;如果转换失败,dynamic_cast 返回一个空指针或引发一个 bad_cast 异常。dynamic_cast 的转换成功的条件是,expression 的类型必须是 type 的公有基类或者 type 的公有派生类,而且 expression 的类型必须包含虚函数,否则编译器会报错。例如,下面的代码可以将一个基类指针转换为一个派生类指针:
class Base {
public:virtual ~Base() {} // virtual destructor
};class Derived : public Base {
public:void foo() { ... } // derived class method
};Base *pb = new Derived(); // base class pointer points to a derived class object
Derived *pd = dynamic_cast<Derived *>(pb); // convert to derived class pointer
if (pd) {pd->foo(); // call derived class method
} 如果 pb 指向的不是一个 Derived 类的对象,那么 pd 将为 nullptr ,无法调用 foo() 方法。
这两个运算符都需要在编译器设置中开启 RTTI 的支持,否则可能会出现运行时错误。但是我们在编译程序时,通常是没有开启 RTTI 支持的。这是因为 RTTI 会增加程序的开销和复杂度,道理很简单,RTTI 需要在编译器和运行时系统中维护额外的类型信息。C++ 作为一个追求效率的语言,默认是没有开启 RTTI 的。
C++ 的 RTTI 机制的优点则是它是一种标准的、跨平台的、内置的类型识别机制,只要编译器支持,就可以使用。
QT 的元对象系统
QT 的元对象系统是一种在 C++ 语言之上的扩展,相较于 RTTI,更加强大,如信号和槽机制、运行时类型信息、动态属性系统等。QT 的元对象系统的核心是 QObject 类,它是所有可以利用元对象系统的类的基类。
还记得我们在 QT 中定义类,通常会继承自 QObject 或其子类,并且还会使用一个奇怪的宏Q_OBJECT。
QObject类定义了一些元数据,如类名、父类名、信号、槽、属性等,这些元数据可以在运行时被访问和操作。而为了启用元对象系统,需要在类声明的私有部分内使用 Q_OBJECT 宏,这个宏会告诉元对象编译器(moc)对这个类进行处理。
元对象编译器(moc)是一个工具,它会扫描源代码中包含 Q_OBJECT 宏的类,提取其中的元数据,并生成相应的元对象代码。这些代码被编译到最终的可执行文件中,供 QT 的运行时系统使用。运行时系统可以通过元对象表来访问和操作对象的元数据,实现信号和槽的连接、动态属性的添加和访问等功能。
信号和槽机制是 QT 的最大特色,它是一种对象间通信的方式。信号和槽都是成员函数,信号是当对象状态发生变化时发出的消息,槽是对信号做出响应的动作。信号和槽可以在不同的对象、不同的线程之间进行连接,实现松耦合的交互。信号和槽的声明和定义都需要使用特定的宏,如 signals、slots、emit等,这些宏会被 moc 转换为元对象代码。例如,下面的代码定义了一个自定义的信号和槽:
class MyWidget : public QWidget {Q_OBJECT // enable meta-object system
public:MyWidget(QWidget *parent = nullptr);...
signals: // declare signalsvoid mySignal(int value); // a custom signal
public slots: // declare slotsvoid mySlot(int value); // a custom slot
};MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {...connect(this, &MyWidget::mySignal, this, &MyWidget::mySlot); // connect signal and slot...
}void MyWidget::mySlot(int value) {// do something when the signal is emitted...
} 动态属性系统是一种在运行时给对象添加和访问属性的方式。属性是对象的一些特征,如颜色、大小、位置等。动态属性系统允许在不修改类定义的情况下,给对象添加新的属性,或者修改已有属性的值。动态属性系统使用 QVariant 类来存储属性的值。
QVariant类是一种通用的数据类型,它可以存储各种类型的值,并在运行时进行类型转换。动态属性系统使用 setProperty() 和 property() 函数来设置和获取属性的值。例如,下面的代码给一个按钮对象添加了一个自定义的属性:
QPushButton *button = new QPushButton("OK");
button->setProperty("myProperty", 123); // add a custom property
int value = button->property("myProperty").toInt(); // get the property value 可以看出,QT 的元对象系统的优点还是比较明显的,它是一种基于 C++ 的、跨平台的、高级的类型识别机制,它可以让程序在运行时获取和操作对象的类型信息,实现对象间的无缝交互,以及在运行时动态地修改对象的行为和外观。
当然,缺点也比较明显,它需要在类声明中使用特殊的宏,对于有代码洁癖的人来说难以忍受。此外还需要使用一个额外的工具(moc)来生成元对象代码,这可能会增加程序的编译时间和复杂度,而且它可能会与一些 C++ 的特性不兼容,如多重继承、模板等。
小结
C++ 的 RTTI 机制和 QT 的元对象系统,这两种机制都可以在运行时获取和操作对象的类型信息,实现对象间的通信和转换。
RTTI 是一种标准的、安全的、内置的类型识别机制,它可以让程序在运行时识别出对象的类型,并进行安全的类型转换。它的缺点是,它会增加程序的开销和复杂度,而且它可能会破坏程序的封装性和抽象性,导致程序的设计不够优雅和灵活。
元对象系统是一种高级的、灵活的、扩展的类型识别机制,它可以让程序在运行时获取和操作对象的类型信息,实现对象间的无缝交互,以及在运行时动态地修改对象的行为和外观。它的缺点是,它需要在类声明中使用特殊的宏,以及使用一个额外的工具(moc)来生成元对象代码,这可能会增加程序的编译时间和复杂度,而且它可能会与一些C++的特性不兼容,如多重继承、模板等。
RTTI 和元对象系统都有各自的优缺点,它们适用于不同的场景和需求。一般来说,如果我们只需要进行简单的类型识别和转换,而且不需要使用信号和槽、动态属性等功能,那么我们可以使用 RTTI 。如果我们需要进行复杂的类型识别和转换,而且需要使用信号和槽、动态属性等功能,那么我们可以使用元对象系统。
当然,如果要使用 QT 的元对象系统,势必需要把 QT 整套框架引入。这里对 C++ 和 QT 的初学者和爱好者提供一些有用的信息和参考,希望对大家有所帮助。
相关文章:
QT 中的元对象系统
作为一名十几年的 C 程序员,最近一段时间使用 QT 开发程序,发现 QT 中还是有许多值得深入理解的技术。QT 不仅仅是一个应用程序开发框架,还有一些对标准 C 的扩充。本文和大家一起探讨 QT 中的元对象系统。 在分析 QT 中的元对象系统之前&…...
在两个java项目中实现Redis的发布订阅模式
如何在两个java项目中实现Redis的发布订阅模式? 1. Redis简介2. 发布订阅模式介绍3. 实现思路4. 代码实现及详细解释4.1. RedisUtil4.2. Publisher4.3. Subscriber4.4. 运行程序 目录: Redis简介发布订阅模式介绍实现思路代码实现及详细解释 1. Redis简…...
执行shell脚本提示syntax error: unexpected end of file
具体报错如下: ./test.sh: line 36: syntax error: unexpected end of file执行命令时需将test.sh替换为实际的脚本文件名称。 情形一: shell脚本在Windows下编写,上传到Linux上执行,由于 fileformat 类型不同,所以报…...
信也科技发布2023年Q3财报:数字金融服务业务增长稳健,持续拉动实体消费
11月21日,信也科技(NYSE:FINV)公布2023年第三季度未经审计的财务报告。财报显示,信也科技三季度在国内、国际市场延续稳健增长态势,实现季度营收31.98亿元(人民币,下同)&…...
Springcloud可视化物联网智慧工地云SaaS平台源码 支持二开和私有化部署
智慧工地平台围绕建筑施工人、物、事的安全管理为核心,对应研发了劳务实名制、视频监控、扬尘监测、起重机械安全监测、安全帽监测等功能一体化管理的解决方案。 智慧工地是聚焦工程施工现场,紧紧围绕人、机、料、法、环等关键要素,综合运用物…...
51单片机应用从零开始(七)·循环语句(if语句,swtich语句)
51单片机应用从零开始(一)-CSDN博客 51单片机应用从零开始(二)-CSDN博客 51单片机应用从零开始(三)-CSDN博客 51单片机应用从零开始(四)-CSDN博客 51单片机应用从零开始(…...
Web服务器(go net/http) 处理Get、Post请求
大家好 我是寸铁👊 总结了一篇Go Web服务器(go net/http) 处理Get、Post请求的文章✨ 喜欢的小伙伴可以点点关注 💝 前言 go http请求如何编写简单的函数去拿到前端的请求(Get和Post) 服务器(后端)接收到请求后,又是怎么处理请求,…...
Unity中颜色空间Gamma与Linear
文章目录 前言一、人眼对光照的自适应1、光照强度与人眼所见的关系2、巧合的是,早期的电子脉冲显示屏也符合这条曲线3、这两条曲线都巧合的符合 y x^2.2^(Gamma2.2空间) 二、Gamma矫正1、没矫正前,人眼看电子脉冲显示屏ÿ…...
Word/PPT/PDF怎么免费转为JPG图片?
1、打开金鸣表格文字识别网站。 2、点击导航条上的“软件下载” 3、安装并打开金鸣表格文字识别软件。 4、点击顶部导航栏的“文件转图片”。 5、选择需要转换成图片的文件(支持Word/PPT/PDF). 6、点“打开”程序将自动分页转换为图片。...
使用docker命令_进入容器_登录mysql服务_并执行sql语句---Docker工作笔记005
今天就用到了,不得不说用docker用到的还是少,记录一下,常用的也就这些吧. 首先执行: docker ps [root@localhost dataease-1.18.9]# docker ps CONTAINER ID IMAGE COMMAND CREATED …...
PMP 考试的含金量怎么样?
这里可以三个思考题和三个价值点帮你认识PMP考试。 三个思维题 1.工作环境 PMP证书含金量的一个很大因素,就是考证的人是否对PMP证书有比较强的实际需求。相反,如果只是听别人说,PMP证书很好,不管工作中是否有需要,…...
2023亚太杯数学建模A题思路代码分析
已经完成A题完整思路代码,文末名片查看获取 A题就是我们机器学习中的一个图像识别,他是水果图像识别,就是苹果识别的一个问题,我们用到的方法基本是使用深度学习中的卷积神经网络来进行识别和分类 问题一:基于附件1中…...
Qt实现自定义IP地址输入控件(百分百还原Windows 10网络地址输入框)
在开发网络相关的程序时,我们经常需要输入IP地址,例如源地址和目标地址。Qt提供了一些基础的控件,如QLineEdit,但是它们并不能满足我们对IP地址输入的要求,例如限制输入的格式、自动跳转到下一个输入框、处理回车和退格键等。因此,我们需要自己编写一个自定义的IP地址输入…...
Linux下的C++ socket编程实例
服务端: 服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。 socket() -> bind() -> listen() -> accept() 客户端: 客户端先初始化socket,然后…...
4.常见面试题--操作系统
特点:并发性、共享性、虚拟性、异步性。 Windows 和 Linux 内核差异 对于内核的架构⼀般有这三种类型: ● 宏内核,包含多个模块,整个内核像⼀个完整的程序; ● 微内核,有⼀个最⼩版本的内核࿰…...
YOLOv8训练自己的目标检测数据集
YOLOv8训练自己的目标检测数据集 目录标题 源码下载环境配置安装包训练自己的数据集数据集文件格式数据集文件配置超参数文件配置训练数据集命令行训练脚本.py文件训练 进行detect显示detect的效果 源码下载 YOLOv8官方的GitHub代码,同时上面也有基础环境的配置要…...
代码随想录算法训练营第三十二天| 122 买卖股票的最佳时机 || 55 跳跃游戏 45 跳跃游戏 ||
目录 122 买卖股票的最佳时机 || 55 跳跃游戏 45 跳跃游戏 || 122 买卖股票的最佳时机 || 设置变量now代表此时买入的股票,为赋值为Integer.MAX_VALUE,遍历prices数组,有如下两种情况: 如果比now小说明不能售出,可以…...
聚类笔记/sklearn笔记:Affinity Propagation亲和力传播
1 算法原理 1.1 基本思想 将全部数据点都当作潜在的聚类中心(称之为 exemplar )然后数据点两两之间连线构成一个网络( 相似度矩阵 )再通过网络中各条边的消息( responsibility 和 availability )传递计算出各样本的聚类中心。 1.2 主要概念 Examplar聚类中心similarity S(i…...
Linux常用操作 Vim一般使用 SSH介绍 SSH密钥登录
目录 1. 常用命令 2. vim一般使用 3. SSH介绍 4. ssh密钥登录 1. 常用命令 1)# 与 $ 提示的区别 # 表示用户有root权限,一般的以root用户登录提示符为#, $提示符表示用户为普通用户 2)ifconfig 查看ip地址 eno1: 代表由主板…...
Hadoop技术与应用的习题
第一章测验 1、下面哪个选项不属于Google的三驾马车? A.HDFS B.MapReduce C.BigTable D.GFS 2、下面哪个思想是为了解决PageRank(网页排名)的问题? A.GFS B.BigTable C.MapReduce D.YARN 3、GFS 存储的文件都被分割成固定大小的…...
用Python和akshare库5分钟搞定LOF基金实时数据抓取与CSV保存(保姆级教程)
零基础Python实战:5分钟自动化获取LOF基金行情数据 最近两年,越来越多的个人投资者开始关注LOF基金的投资机会。这类基金既能在场内像股票一样交易,又能通过场外渠道申购赎回,流动性优势明显。但很多新手在跟踪LOF基金行情时&…...
从AFLW到300W-LP:头部姿态估计数据集怎么选?实战避坑与数据预处理指南
从AFLW到300W-LP:头部姿态估计数据集实战选择与预处理全攻略 当你第一次打开AFLW2000-3D数据集时,可能会被那些夸张的头部角度震惊——从几乎90度的侧脸到夸张的俯仰,这些数据真的适合训练一个驾驶员监控模型吗?作为计算机视觉领域…...
JavaSE-12-Java多线程零基础入门核心概念精讲
目录 一、进程与线程:结合SpringBoot实战场景彻底搞懂 1.1 新手必答三大疑问(结合开发日常) 1.2 进程核心概念通俗理解 1.3 线程核心概念通俗理解 1.4 进程与线程核心区别 1.5 Java线程底层运行机制实操演示代码 实操代码:查…...
CoCo框架:代码驱动的文本到图像生成技术解析
1. 项目概述CoCo(Code-as-CoT)是一种创新的文本到图像(T2I)生成框架,它将传统的自然语言链式思考(CoT)推理过程转化为可执行代码,从而实现对生成图像结构化布局的精确控制。该框架由…...
Copilot Next 自动化工作流配置终极复盘(附可运行Demo仓库):涵盖Azure DevOps/Bitbucket/GitLab三平台适配,仅剩最后87份源码包
更多请点击: https://intelliparadigm.com 第一章:VS Code Copilot Next 自动化工作流配置 面试题汇总 VS Code Copilot Next 作为 GitHub 官方深度集成的下一代智能编程助手,其自动化工作流配置能力已成为前端与全栈工程师面试中的高频考点…...
EASY-HWID-SPOOFER:内核级硬件指纹伪装架构设计与实现原理
EASY-HWID-SPOOFER:内核级硬件指纹伪装架构设计与实现原理 【免费下载链接】EASY-HWID-SPOOFER 基于内核模式的硬件信息欺骗工具 项目地址: https://gitcode.com/gh_mirrors/ea/EASY-HWID-SPOOFER 在当今数字安全领域,硬件指纹追踪已成为用户隐私…...
PumpClaw:为AI智能体构建去中心化收入基础设施的完整指南
1. 项目概述:为AI智能体构建自主收入基础设施在区块链和人工智能的交汇点上,一个核心的挑战日益凸显:自主运行的AI智能体如何像人类一样,拥有可持续的、无需许可的收入来源?传统的商业模式依赖于中心化的支付网关、繁琐…...
Revelation光影包:为Minecraft打造电影级物理渲染体验
Revelation光影包:为Minecraft打造电影级物理渲染体验 【免费下载链接】Revelation An explorative shaderpack for Minecraft: Java Edition 项目地址: https://gitcode.com/gh_mirrors/re/Revelation 想要将Minecraft的方块世界升级为电影大片般的视觉盛宴…...
INCA软件配置优化:3秒搞定初始化,告别20秒漫长等待(附快捷键大全)
INCA软件配置优化:3秒搞定初始化,告别20秒漫长等待(附快捷键大全) 在汽车电子测试领域,效率就是生产力。每次测试任务中节省的17秒,乘以数百次的重复操作,可能意味着项目周期缩短数天。本文将深…...
第四章:TTM分析: 4.8.2 TTM Eviction 选择策略: LRU 与候选筛选
前置阅读: TTM Eviction 机制概述与触发流程 本文解析 TTM Eviction 四步走中的 Step 2: 选择策略 – 从 VRAM 的 LRU 链表中,按什么规则选出被驱逐的 victim BO。 1. 核心问题 VRAM 不够用了,需要踢一个 BO 出去。但并不是所有 BO 都能踢: pinned 的 BO 不能踢 (被钉住了) …...
