当前位置: 首页 > news >正文

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 程序员&#xff0c;最近一段时间使用 QT 开发程序&#xff0c;发现 QT 中还是有许多值得深入理解的技术。QT 不仅仅是一个应用程序开发框架&#xff0c;还有一些对标准 C 的扩充。本文和大家一起探讨 QT 中的元对象系统。 在分析 QT 中的元对象系统之前&…...

在两个java项目中实现Redis的发布订阅模式

如何在两个java项目中实现Redis的发布订阅模式&#xff1f; 1. Redis简介2. 发布订阅模式介绍3. 实现思路4. 代码实现及详细解释4.1. RedisUtil4.2. Publisher4.3. Subscriber4.4. 运行程序 目录&#xff1a; Redis简介发布订阅模式介绍实现思路代码实现及详细解释 1. Redis简…...

执行shell脚本提示syntax error: unexpected end of file

具体报错如下&#xff1a; ./test.sh: line 36: syntax error: unexpected end of file执行命令时需将test.sh替换为实际的脚本文件名称。 情形一&#xff1a; shell脚本在Windows下编写&#xff0c;上传到Linux上执行&#xff0c;由于 fileformat 类型不同&#xff0c;所以报…...

信也科技发布2023年Q3财报:数字金融服务业务增长稳健,持续拉动实体消费

11月21日&#xff0c;信也科技&#xff08;NYSE&#xff1a;FINV&#xff09;公布2023年第三季度未经审计的财务报告。财报显示&#xff0c;信也科技三季度在国内、国际市场延续稳健增长态势&#xff0c;实现季度营收31.98亿元&#xff08;人民币&#xff0c;下同&#xff09;&…...

Springcloud可视化物联网智慧工地云SaaS平台源码 支持二开和私有化部署

智慧工地平台围绕建筑施工人、物、事的安全管理为核心&#xff0c;对应研发了劳务实名制、视频监控、扬尘监测、起重机械安全监测、安全帽监测等功能一体化管理的解决方案。 智慧工地是聚焦工程施工现场&#xff0c;紧紧围绕人、机、料、法、环等关键要素&#xff0c;综合运用物…...

51单片机应用从零开始(七)·循环语句(if语句,swtich语句)

51单片机应用从零开始&#xff08;一&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;二&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;三&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;四&#xff09;-CSDN博客 51单片机应用从零开始&#xff08;…...

Web服务器(go net/http) 处理Get、Post请求

大家好 我是寸铁&#x1f44a; 总结了一篇Go Web服务器(go net/http) 处理Get、Post请求的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 go http请求如何编写简单的函数去拿到前端的请求(Get和Post) 服务器(后端)接收到请求后&#xff0c;又是怎么处理请求&#xff0c…...

Unity中颜色空间Gamma与Linear

文章目录 前言一、人眼对光照的自适应1、光照强度与人眼所见的关系2、巧合的是&#xff0c;早期的电子脉冲显示屏也符合这条曲线3、这两条曲线都巧合的符合 y x^2.2^&#xff08;Gamma2.2空间&#xff09; 二、Gamma矫正1、没矫正前&#xff0c;人眼看电子脉冲显示屏&#xff…...

Word/PPT/PDF怎么免费转为JPG图片?

1、打开金鸣表格文字识别网站。 2、点击导航条上的“软件下载” 3、安装并打开金鸣表格文字识别软件。 4、点击顶部导航栏的“文件转图片”。 5、选择需要转换成图片的文件&#xff08;支持Word/PPT/PDF&#xff09;. 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证书含金量的一个很大因素&#xff0c;就是考证的人是否对PMP证书有比较强的实际需求。相反&#xff0c;如果只是听别人说&#xff0c;PMP证书很好&#xff0c;不管工作中是否有需要&#xff0c;…...

2023亚太杯数学建模A题思路代码分析

已经完成A题完整思路代码&#xff0c;文末名片查看获取 A题就是我们机器学习中的一个图像识别&#xff0c;他是水果图像识别&#xff0c;就是苹果识别的一个问题&#xff0c;我们用到的方法基本是使用深度学习中的卷积神经网络来进行识别和分类 问题一&#xff1a;基于附件1中…...

Qt实现自定义IP地址输入控件(百分百还原Windows 10网络地址输入框)

在开发网络相关的程序时,我们经常需要输入IP地址,例如源地址和目标地址。Qt提供了一些基础的控件,如QLineEdit,但是它们并不能满足我们对IP地址输入的要求,例如限制输入的格式、自动跳转到下一个输入框、处理回车和退格键等。因此,我们需要自己编写一个自定义的IP地址输入…...

Linux下的C++ socket编程实例

服务端&#xff1a; 服务器端先初始化socket&#xff0c;然后与端口绑定&#xff0c;对端口进行监听&#xff0c;调用accept阻塞&#xff0c;等待客户端连接。 socket() -> bind() -> listen() -> accept() 客户端&#xff1a; 客户端先初始化socket&#xff0c;然后…...

4.常见面试题--操作系统

特点&#xff1a;并发性、共享性、虚拟性、异步性。 Windows 和 Linux 内核差异 对于内核的架构⼀般有这三种类型&#xff1a; ● 宏内核&#xff0c;包含多个模块&#xff0c;整个内核像⼀个完整的程序&#xff1b; ● 微内核&#xff0c;有⼀个最⼩版本的内核&#xff0…...

YOLOv8训练自己的目标检测数据集

YOLOv8训练自己的目标检测数据集 目录标题 源码下载环境配置安装包训练自己的数据集数据集文件格式数据集文件配置超参数文件配置训练数据集命令行训练脚本.py文件训练 进行detect显示detect的效果 源码下载 YOLOv8官方的GitHub代码&#xff0c;同时上面也有基础环境的配置要…...

代码随想录算法训练营第三十二天| 122 买卖股票的最佳时机 || 55 跳跃游戏 45 跳跃游戏 ||

目录 122 买卖股票的最佳时机 || 55 跳跃游戏 45 跳跃游戏 || 122 买卖股票的最佳时机 || 设置变量now代表此时买入的股票&#xff0c;为赋值为Integer.MAX_VALUE&#xff0c;遍历prices数组&#xff0c;有如下两种情况&#xff1a; 如果比now小说明不能售出&#xff0c;可以…...

聚类笔记/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&#xff09;# 与 $ 提示的区别 # 表示用户有root权限&#xff0c;一般的以root用户登录提示符为#&#xff0c; $提示符表示用户为普通用户 2&#xff09;ifconfig 查看ip地址 eno1: 代表由主板…...

Hadoop技术与应用的习题

第一章测验 1、下面哪个选项不属于Google的三驾马车&#xff1f; A.HDFS B.MapReduce C.BigTable D.GFS 2、下面哪个思想是为了解决PageRank&#xff08;网页排名&#xff09;的问题&#xff1f; A.GFS B.BigTable C.MapReduce D.YARN 3、GFS 存储的文件都被分割成固定大小的…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...