Qt五大核心特性之元对象系统
前言
Qt 的元对象系统(Meta-Object System)是 Qt 框架的核心之一,提供了一些 C++ 原生不具备的功能(因为在C++它们是静态的),如反射、信号槽机制、属性系统等。通过这个系统,Qt 实现了许多强大的功能,这使得它成为一个更易于使用和扩展的框架。
正文
元对象系统
1. 元对象系统的组成部分
1.1 Q_OBJECT 宏
- Q_OBJECT 是元对象系统的入口。任何需要使用元对象系统功能的类都必须包含这个宏。
- 它通常放在类的私有部分的顶部,并由 Qt 的元对象编译器(moc)处理,生成与类相关的元数据和代码。
class MyClass : public QObject {Q_OBJECTpublic:MyClass(QObject *parent = nullptr) : QObject(parent) {}signals:void mySignal();public slots:void mySlot();
};
1.2 QMetaObject
- 元对象
QMetaObject是用于描述另一个对象结构的对象,它提供了关于 QObject 类及其子类的元数据(如类名、信号、槽、属性等)。 - 可以通过调用
QObject::metaObject()来获取与对象相关的元对象。
const QMetaObject *meta = myObject->metaObject();
qDebug() << "Class name:" << meta->className();
1.3 信号和槽(Signals and Slots)
- 信号槽机制是 Qt 中的核心通信方式。信号(signal)是用来发出事件通知的,而槽(slot)是用来处理这些事件的。
signals:和slots:关键字标识了类中的信号和槽函数,信号槽的连接可以在编译时或运行时完成。
QObject::connect(sender, &SenderClass::signalName, receiver, &ReceiverClass::slotName);
1.4 属性系统(Property System)
- 属性系统使得可以通过字符串名称访问和操作对象的属性,这在 QML 和动画系统中尤其有用。
- 使用
Q_PROPERTY宏来定义属性。
class MyClass : public QObject {Q_OBJECT// 意思是value通过setValue这个函数来更新/设置这个值,在更新后发出通知信号valueChanged(int) // 当属性值发生改变时,这个信号会被发出,通知所有连接到该信号的槽函数Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)public:int value() const { return m_value; }void setValue(int value) {if (m_value == value)return;m_value = value;emit valueChanged(m_value);}signals:void valueChanged(int newValue);private:int m_value;
};
1.5 QMetaObject::invokeMethod
- 可以在运行时使用
QMetaObject::invokeMethod()来调用对象的槽函数或其它成员函数。
MyClass obj;
QMetaObject::invokeMethod(&obj, "mySlot");
2. 元对象编译器(moc)
Qt 的元对象编译器 moc 是解析带有 Q_OBJECT 宏的文件。moc 若发现一个或多个包含了 Q_OBJECT 宏的类的声明,则会生成另外一个包含了Q_OBJECT 宏实现代码的 C++源文件(该源文件通常名称为 moc_*.cpp) ,这个新的源文件要么被#include 包含到类的源文件中,要么被编译键接到类的实现中(通常是使用的此种方法)。注意:新文件不会“替换”掉旧的文件,而是与原文件一起编译
moc主要做了一下工作
- 生成一个静态的元对象实例,该实例包含类的元信息。
- 为每个信号生成一个函数,该函数可以发射该信号。
- 为类生成一个静态的成员函数,该函数可以返回静态的元对象实例。
3.反射机制
反射(Reflection)指的是程序在运行时检查和操作自身结构的能力。C++ (C++17好像支持,但是和Qt中的不同)本身不支持反射,但 Qt 通过元对象系统提供了一定程度的反射能力。这种能力主要体现在以下几个方面:
-
动态类型信息:
- 使用
QObject::metaObject()可以在运行时获取与类相关的元数据(如类名、信号、槽、属性等)。
- 使用
-
动态属性访问:
- 通过
QObject::setProperty()和QObject::property()方法,可以通过字符串名称在运行时访问和修改对象的属性。
- 通过
-
信号与槽的动态连接:
- 使用
QObject::connect()函数,可以在运行时通过字符串名称来动态连接信号和槽。这使得信号和槽的连接可以在运行时根据条件来建立或改变。
- 使用
-
动态对象创建:
- 使用
QMetaObject::newInstance()可以在运行时根据类的元对象创建新的对象实例(前提是类中有符合条件的构造函数)。
- 使用
Qt 的元对象系统通过元对象编译器(moc)生成附加的代码,允许在运行时获取类的元数据,并使用这些元数据实现类似反射的功能。这种机制在实现动态特性、插件系统和QML绑定等功能时非常有用。
4. 元对象系统的使用
元对象系统的使用需要满足三个条件
- 该类必须继承自
QObject或者继承自继承QObject类的子类 - 该类在声明
Q_OBJECT这个宏时,必须在私有区域进行声明 - 元对象编译器(moc)为每个
QObject的子类,提供了实现员特性所必须的代码
MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H#include <QObject>
#include <QDebug>// MyClass 是一个示例类,展示了 Qt 元对象系统的使用
class MyClass : public QObject {Q_OBJECT// 定义一个属性 "value",可以通过 getter (value) 和 setter (setValue) 访问,// 当属性值发生变化时发出信号 valueChangedQ_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)public:explicit MyClass(QObject *parent = nullptr);// 属性的 getter 函数int value() const;// 属性的 setter 函数void setValue(int newValue);signals:// 当属性值发生变化时发出的信号void valueChanged(int newValue);public slots:// 一个槽函数,用于打印当前属性值void printValue();private:int m_value; // 用于存储属性值的成员变量
};#endif // MYCLASS_H
MyClass.cpp
#include "MyClass.h"// 构造函数,初始化属性值为 0
MyClass::MyClass(QObject *parent) : QObject(parent), m_value(0) {}// getter 函数,返回当前的属性值
int MyClass::value() const {return m_value;
}// setter 函数,设置属性值,并发出 valueChanged 信号(如果值发生变化)
void MyClass::setValue(int newValue) {if (m_value != newValue) {m_value = newValue;emit valueChanged(m_value);}
}// 槽函数,打印当前属性值
void MyClass::printValue() {qDebug() << "The value is:" << m_value;
}
main.cpp
#include <QCoreApplication>
#include <QMetaObject>
#include <QMetaProperty>
#include <QMetaMethod>
#include "MyClass.h"int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 动态创建 MyClass 对象QObject *obj = QMetaObject::newInstance(MyClass::staticMetaObject);// 检查对象是否创建成功if (!obj) {qDebug() << "Failed to create the object!";return -1;}// 获取对象的元对象信息const QMetaObject *metaObj = obj->metaObject();qDebug() << "Class Name:" << metaObj->className();// 动态访问和修改属性int propertyIndex = metaObj->indexOfProperty("value");if (propertyIndex != -1) {obj->setProperty("value", 42); // 设置属性值qDebug() << "Property 'value':" << obj->property("value").toInt(); // 获取属性值}// 动态连接信号和槽int signalIndex = metaObj->indexOfSignal("valueChanged(int)");int slotIndex = metaObj->indexOfSlot("printValue()");if (signalIndex != -1 && slotIndex != -1) {QMetaObject::connect(obj, signalIndex, obj, slotIndex);}// 修改属性值,这将触发 valueChanged 信号,并调用 printValue 槽函数obj->setProperty("value", 100);// 清理动态创建的对象delete obj;return a.exec();
}
代码解释
MyClass.h
Q_OBJECT: 必须放在类的定义中,用于启用 Qt 的元对象系统。Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged): 定义一个名为value的属性,指定了 getter (value)、setter (setValue) 和属性变化时发出的信号 (valueChanged)。signals:: 定义信号valueChanged,当value属性发生变化时发出。public slots:: 定义槽函数printValue,用于打印属性值。
MyClass.cpp
MyClass::MyClass(QObject *parent): 构造函数,初始化m_value为 0。value(): 返回当前的m_value。setValue(int newValue): 设置m_value的值,并在值发生变化时发出valueChanged信号。printValue(): 打印当前的m_value。
main.cpp
QMetaObject::newInstance(MyClass::staticMetaObject): 动态创建MyClass的实例。MyClass::staticMetaObject提供了类的元对象信息。metaObject()->className(): 获取并打印类名。metaObject()->indexOfProperty("value"): 获取属性value的索引。通过索引动态设置和获取属性值。QMetaObject::connect(): 动态连接信号valueChanged(int)和槽printValue()。obj->setProperty("value", 100): 修改属性值,触发valueChanged信号,进而调用printValue槽函数。
注意:若定义了QObject类的派生类,并进行了构建,在这之后再添加 Q_OBJECT 宏,则此时
必须执行一次 qmake 命令(“构建”>“执行 qmake”),否则 moc 不能生成代码。
相关文章:
Qt五大核心特性之元对象系统
前言 Qt 的元对象系统(Meta-Object System)是 Qt 框架的核心之一,提供了一些 C 原生不具备的功能(因为在C它们是静态的),如反射、信号槽机制、属性系统等。通过这个系统,Qt 实现了许多强大的功能,这使得它…...
开放式耳机伤耳朵吗?开放式耳机在一定程度上保护我们的耳朵
开放式耳机通常被认为对耳朵的伤害较小,因为它们不需要插入耳道,从而减少了耳道内的压力和潜在的感染风险。与传统入耳式耳机相比,开放式耳机允许耳朵自然通风,减少耳道内的湿气和热量积聚,这有助于保持耳朵的健康。 然…...
JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车系统源码
🚗💨打车、顺风车、滴滴车&跑腿系统,一键解决出行生活难题! 一、出行新选择,打车从此不再难 忙碌的生活节奏,让我们常常需要快速、便捷的出行方式。打车、顺风车、滴滴车系统,正是为了满足…...
批量智慧:揭秘机器学习中的批量大小
标题:批量智慧:揭秘机器学习中的批量大小 机器学习是人工智能的一个分支,它使得计算机能够从数据中学习并做出决策或预测。在机器学习的过程中,批量大小(Batch Size)是一个至关重要的超参数,它…...
苹果Vision Pro生态发展:现状、挑战与未来展望
苹果公司以其创新技术和强大的生态系统闻名于世。在最近的财报会议上,CEO蒂姆库克分享了Vision Pro平台的最新进展,引发了业界的广泛关注。本文将深入探讨Vision Pro生态的现状、面临的挑战以及与其他XR平台的对比分析。 一、Vision Pro生态现状 据库克介绍,Vision Pro平台…...
湖南第一师范学院来访炼石,推动密码与数据安全合作
2024年8月11日,为进一步加强交流与合作,深入探讨校企产学研合作,湖南第一师范学院计算机学院院长杨恒伏一行莅临炼石调研指导。湖南第一师范学院计算机学院院长杨恒伏、网络空间安全系主任周聪等专家领导出席。炼石网络创始人兼CEO白小勇对湖…...
全面解析ETL:数据仓库架构中的关键处理过程
目录 一、数据仓库架构中的ETL 二、数据抽取 (1)逻辑抽取 (2)物理抽取 (3)变化数据捕获 三、数据转换 四、数据装载 (1)提高装载效率 (2)处理装载失败 五、ET…...
keepalived的介绍与配置
Keepalived是一个轻量级别的高可用解决方案,同时也是一个免费开源的、用C编写的类似于layer3, 4 & 7(也有说法认为是layer3, 4 & 5)交换机制的软件,主要提供负载均衡和高可用服务。它自动完成检测服务器的状态、故障隔离和…...
二叉树概念与使用
文章目录 一、作用二、二叉树概念特征2.1二叉树概念补充2.1.1度2.1.2深度2.1.3若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1个结点 三、使用2.1二叉树存储,检索,插入项目 四、 二叉树检索的时间复杂度1. 普通二叉树2. 二叉搜…...
MongoDB 在 Java 中的使用教程
目录 MongoDB 简介环境准备使用 Java 连接 MongoDB基本 CRUD 操作复杂查询操作索引和性能优化事务管理总结 1. MongoDB 简介 MongoDB 是一个基于分布式文件存储的 NoSQL 数据库系统。它以文档(JSON 形式)存储数据,具有高扩展性和灵活的数据…...
微前端架构下的配置管理:策略、实现与最佳实践
微前端架构通过将一个大型前端应用拆分为多个小型、自治的子应用,提升了开发效率和应用的可维护性。然而,随着应用规模的扩大和子应用数量的增加,配置管理变得日益复杂。本文将详细介绍在微前端架构下实现应用配置管理的策略、实现方法和最佳…...
React Native中好用的UI组件库
文章目录 前言1.React Native ElementsStar数超24K地址 2.React Native UI KittenStar数超20K地址 3.NativeBaseStar数超20K地址 前言 下面是React Native中一些常用的UI库 1.React Native Elements Star数超24K 官方介绍 React Native Elements 的目标是提供一套用于在 Rea…...
WebSocket 快速入门
WebSocket是什么 WebSocket 是基于 TCP 的一种新的应用层网络协议。它实现了浏览器与服务器全双工通信,即允许服务器主动发送信息给客户端。因此,在 WebSocket 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性…...
MySQL中的存储文件和IO机制详细解析
MySQL中的存储文件和IO机制详细解析 一、引言 MySQL作为广泛使用的关系型数据库管理系统,凭借其高性能和稳定性在各大应用中扮演了关键角色。在实际应用中,数据库需要对大量数据进行存储、检索、更新等操作。这些操作离不开底层的文件存储系统…...
复习之 java 锁
裁员在家,没有面试机会,整理整理面试知识点吧! 不得不知道的java 锁 Java 中,提供了两种方式来实现同步互斥访问(也就是锁):synchronized 和 Lock 多线程编程中,有可能会出现多个线…...
数据结构与算法 - 图
一、概念 图是有顶点(vertex)和边(edge)组成的数据结构,例如 该图有4个顶点:A、B、C、D以及四条有向边,有向图中,边是单向的。 1. 有向图 VS 无向图 如果是无向图,那么…...
白骑士的HTML教学基础篇 1.1 HTML简介
在现代互联网的世界里,HTML(HyperText Markup Language)是所有网页的基础语言。无论是简约的个人博客还是复杂的商业网站,HTML都扮演着不可或缺的角色。掌握HTML是学习前端开发的第一步,这不仅能够帮助你构建静态网页&…...
c语言基础知识学习
1. C 语言简介 定义:C 语言是一种过程式编程语言,设计用于系统编程和应用程序开发。特点:高效、灵活、接近硬件,支持指针和内存操作。 1. 基本语法 程序结构: C 语言程序由函数组成,main 函数是程序的入口…...
Qt/QML学习-Dial
QML学习 Dial例程视频讲解代码 main.qml import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15Window {width: 640height: 480visible: truetitle: qsTr("Hello World")Dial {anchors.fill: parentid: dial// 设置旋钮的范围from: 0to: …...
达梦数据库系列—48.DMHS实现Mysql到DM8的同步
目录 DMHS实现Mysql到DM8的同步 1、准备介质 2、安装 3、准备源端Mysql和目标端DM8 软件安装 数据库创建 打开归档 开启附加日志 创建辅助表 Mysql客户端驱动 Mysql端安装ODBC 检查依赖包 创建连接用户 创建测试表 4、同步配置 修改服务配置 Mysql到Dm单向同步…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
