【Qt开发流程】之元对象系统

描述

Qt的元对象系统(Meta-Object System)是Qt框架的核心机制之一,它提供了运行时类型信息(RTTI)和信号与槽(Signals and Slots)机制的支持。元对象系统在Qt中扮演了很重要的角色,它使得Qt能够实现许多强大的功能,例如信号与槽的自动连接、QObject树结构的管理、对象的属性、对象之间的消息通信等。

元对象系统支持以下机制:

  1. 对象的类型信息:元对象系统允许在运行时获取对象的类型信息,包括类名、父类名、属性信息、信号与槽函数等。这使得我们可以在运行时通过对象指针来查询和操作对象的属性和函数。

  2. 信号与槽机制:元对象系统支持Qt独有的信号与槽机制,它提供了一种灵活、类型安全的方式来实现对象间的通信通过信号与槽,一个对象可以触发一个信号,而其他对象可以连接到该信号并执行相应的槽函数。

  3. QMetaObject类:QMetaObject是元对象系统的核心类之一,它包含了类元信息,如类名、父类名、属性、信号与槽等。我们可以通过QMetaObject来查询和操作类的元信息,例如获取属性的值、连接信号与槽等。

  4. Q_OBJECT宏:在使用元对象系统时,需要在类的声明中添加Q_OBJECT宏。它会自动生成元信息,并使得类具备信号与槽的功能。在构建项目时,moc(元对象编译器)会通过预器解析源代码,生成相关的元信息。

  5. 对象树结构管理:元对象系统支持QObject树结构的管理,即对象的父子关系。当一个QObject对象具有其他QObject对象作为其对象时,它会负责管理子对象的生命周期,并在其自身被销毁时自动销毁子对象。

总而言之,言而总之,元对象系统是Qt强大功能的基石,它不仅提供了类的元信息,还支持信号与槽机制、属性系统、对象树管理等重要功能。通过元对象系统,开发者更加方便地完成杂的应用程序开发,并实现可扩展和可维护的代码结构。

moc工具

Qt提供的moc(Meta Object Compiler)工具主要用于实现Qt中的元对象系统(Meta-Object System)。

在Qt中,元对象系统允许程序在运行时获取对象的属性、方法和信号,并且可以动态连接信号和槽。这是Qt框架的重要特性之一。moc工具的作用就是将使用了特殊宏的类或者函数处理成C++代码,为Qt的元对象系统提供必要的信息。

moc工具读取一个c++源文件。如果它发现一个或多个包含Q_OBJECT宏的类声明,它会生成另一个c++源文件,其中包含每个类的元对象代码。生成的源文件要么#include到类的源文件中,要么(更常见的是)编译并链接到类的实现中。

当我们在使用信号与槽、Q_OBJECT 宏、动态元属性、Q_DECLARE_INTERFACE 宏等 Qt 特有的功能时,就需要使用moc工具来处理相关的代码文件。moc会生成一个额外的源文件,其中包含元对象系统所需的信息,并且在构建时将其编译成目标文件。这样就可以在运行时使用元对象系统,实现Qt框架的各项功能。

总而言之,言而总之,moc工具是Qt框架元对象系统实现的重要组成部分,它为Qt应用程序提供了强大的动态功能和元数据支持。

实现

元对象系统基于以下三点:

  • QObject类为可以利用元对象系统的对象提供了一个基类。
  • 类声明的私有部分中的Q_OBJECT宏用于启用元对象特性,例如动态属性、信号和槽。
  • 元对象编译器(moc)为每个QObject子类提供实现元对象特性所需的代码。

其他特性

除了提供对象之间通信的信号和槽机制(引入该系统的主要原因)之外,元对象代码还提供以下附加功能:

  • QObject::metaObject()返回类的关联元对象。
  • QMetaObject::className()在运行时以字符串形式返回类名,不需要通过c++编译器支持本机运行时类型信息(RTTI)。
  • QObject::inherits()函数返回一个对象是否是继承了QObject继承树中指定类的类的实例。
  • QObject::tr()和QObject::trUtf8()翻译字符串用于国际化。
  • QObject::setProperty()和QObject::property()通过名称动态设置和获取属性。
  • QMetaObject::newInstance()构造一个类的新实例。

qobject_case()转换

可以使用qobject_cast()对QObject类执行动态强制转换。qobject_cast()函数的行为类似于标准c++的dynamic_cast(),其优点是不需要RTTI支持,并且可以跨动态库边界工作。它试图将其参数强制转换为尖括号中指定的指针类型,如果对象的类型是正确的(在运行时确定),则返回非零指针,如果对象的类型不兼容则返回0。
例如,让我们假设MyWidget继承自QWidget,并使用Q_OBJECT宏声明:

QObject *obj = new MyWidget;

QObject *类型的obj变量实际上引用了一个MyWidget对象,因此我们可以适当地强制转换它:

QWidget *widget = qobject_cast<QWidget *>(obj);

从QObject到QWidget的强制转换是成功的,因为对象实际上是一个MyWidget,它是QWidget的一个子类。既然我们知道obj是一个MyWidget,我们也可以将它强制转换为MyWidget *:

MyWidget * MyWidget = qobject_cast<MyWidget *>(obj);

对MyWidget的强制转换是成功的,因为qobject_cast()没有区分内置Qt类型和自定义类型。

QLabel *label = qobject_cast<QLabel *>(obj);
// label is 0

另一方面,对QLabel的强制转换失败。然后将指针设置为0。这使得在运行时基于类型以不同方式处理不同类型的对象成为可能:

      if (QLabel *label = qobject_cast<QLabel *>(obj)) {label->setText(tr("Ping"));} else if (QPushButton *button = qobject_cast<QPushButton *>(obj)) {button->setText(tr("Pong!"));}

虽然可以在不使用Q_OBJECT宏和元对象代码的情况下使用QObject作为基类,但是如果不使用Q_OBJECT宏,信号和槽以及这里描述的其他特性都将不可用。从元对象系统的角度来看,一个没有元代码的QObject子类相当于它最近的祖先带有元对象代码。这意味着,例如,QMetaObject::className()将不会返回类的实际名称,而是这个祖先的类名。
因此,强烈建议QObject的所有子类都使用Q_OBJECT宏,不管它们是否实际使用信号、槽和属性。

示例

.h

class MyObject : public QObject
{Q_OBJECTQ_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)public:MyObject(QObject *parent = nullptr) : QObject(parent), m_name(""), m_age(0) {}QString name() const { return m_name; }void setName(const QString &name) {if (m_name != name) {m_name = name;emit nameChanged();}}int age() const { return m_age; }void setAge(int age) {if (m_age != age) {m_age = age;emit ageChanged();}}signals:void nameChanged();void ageChanged();public slots:void printInfo() {qDebug() << "Name:" << m_name << "Age:" << m_age;}private:QString m_name;int m_age;
};

.cpp

    MyObject obj;obj.setObjectName("myObject");const QMetaObject *metaObj = obj.metaObject();int nameIdx = metaObj->indexOfProperty("name");if (nameIdx != -1) {QMetaProperty namePty = metaObj->property(nameIdx);if (namePty.isReadable()) {qDebug() << "Name:" << namePty.read(&obj).toString();}if (namePty.isWritable()) {namePty.write(&obj, QVariant::fromValue(QString("Alice")));qDebug() << "Set Name:" << obj.property("name").toString();}}int ageIdx = metaObj->indexOfProperty("age");if (ageIdx != -1) {QMetaProperty agePty = metaObj->property(ageIdx);if (agePty.isReadable()) {qDebug() << "Age:" << agePty.read(&obj).toInt();}if (agePty.isWritable()) {agePty.write(&obj, QVariant::fromValue(30));qDebug() << "Set Age:" << obj.property("age").toInt();}}int printIdx = metaObj->indexOfMethod("printInfo()");if (printIdx != -1) {QMetaMethod printMethod = metaObj->method(printIdx);printMethod.invoke(&obj);}
  • 使用元对象系统获取 “MyObject” 类的属性和方法
  • 使用 QMetaObject::indexOfProperty() 和 QMetaObject::property() 来获取属性
  • 使用 QMetaObject::indexOfMethod() 和 QMetaObject::method() 来获取方法
  • 可以在运行时读取和写入属性,或调用方法。

结果:
在这里插入图片描述

结论

如果今天生活欺骗了你,不要悲伤,不要哭泣,因为明天生活也会欺骗你

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/255751.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Matlab完成窗口与子窗口

目录 一、前言 二、主窗口与主窗口按钮 三、子窗口 四、调用函数并显示在子窗口中的文本框中 五、关闭子窗口 一、前言 有时候需要借用Matlab完成一个图窗功能&#xff0c;但是我们的程序不仅拥有功能&#xff0c;还拥有一些子功能&#xff0c;那么我们该如何借助Matlab完…

通过K8S安装人大金仓数据库

1. 离线下载镜像&#xff0c;请点击 2. 官网下载镜像 https://www.kingbase.com.cn/xzzx/index.htm&#xff0c;根据自己的需求下载对应版本。 3. K8S需要的yaml清单 cat > kingbase.yaml << EOF apiVersion: apps/v1 kind: Deployment metadata:name: kingbase-…

每日OJ题_算法_双指针③_力扣202. 快乐数

目录 力扣202. 快乐数 解析代码 力扣202. 快乐数 202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 难度 简单 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和…

Peter算法小课堂—贪心算法

课前思考&#xff1a;贪心是什么&#xff1f;贪心如何“贪”&#xff1f; 课前小视频&#xff1a;什么是贪心算法 - 知乎 (zhihu.com) 贪心 贪心是一种寻找最优解问题的常用方法。 贪心一般将求解过程分拆成若干个步骤&#xff0c;自顶向下&#xff0c;解决问题 太戈编程第…

yum源不起作用_yum无法安装程序_Linux默认源替换---Linux工作笔记067

今天在一台机器上进行安装yum install的时候提示,yum不可用,这时候,折腾了一会 后来更换了默认源就可以了. 首先: 可以看到原来的里面有个 yum.repos.d 里面放了很多源,但是这些源是不可以联网的. 是内网的源,所以,我对他进行了 mv yum.repos.d yum.repos.d.bak 重命名 然…

pycharm中py文件设置参数

在py文件中右键 直接对应复制进去即可

openGauss学习笔记-147 openGauss 数据库运维-备份与恢复-逻辑备份与恢复之gs_dump

文章目录 openGauss学习笔记-147 openGauss 数据库运维-备份与恢复-逻辑备份与恢复之gs_dump147.1 背景信息147.2 注意事项147.3 语法147.4 参数说明147.4.1 通用参数&#xff1a;147.4.2 转储参数&#xff1a;147.4.3 连接参数&#xff1a; 147.5 说明147.6 示例 openGauss学习…

Windows故障排除 – 连接WiFi却无法上网

Windows故障排除 – 连接WiFi却无法上网 Windows Troubleshooting - Connecting WiFi but PC Cannot Browse Internet By JacksonML 有个同学买了一台崭新的D品牌游戏本&#xff0c;i7处理器&#xff0c;英伟达RTX系列独立显卡及15寸液晶显示器&#xff0c;可谓功能强大。但是…

Ubuntu宝塔面板本地部署轻论坛系统HadSky并远程访问

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

系列八、SpringBoot中自定义SpringMVC配置

一、概述 作为Spring家族的明星产品&#xff0c;SpringBoot极大地简化了程序员的日常开发&#xff0c;提高了开发效率。我们很容易得借助于SpringBoot就可以快速开发业务代码。一般情况下&#xff0c;公司的日常开发都是基于web服务的&#xff0c;我们在使用idea等工具初始化一…

JavaScript如何实现按键音效、视频播放,标签分类切换横向滚动

1.使用HTML5的audio标签 &#xff08;音频播放&#xff09; <audio id"click-sound"><source src"audio/show.mp3" type"audio/mpeg"> </audio> <button id"button">按钮</button> var clickSound d…

Spring Boot的日志

打印日志 打印日志的步骤: • 在程序中得到日志对象. • 使用日志对象输出要打印的内容 在程序中得到日志对象 在程序中获取日志对象需要使用日志工厂LoggerFactory,代码如下: package com.example.demo;import org.slf4j.Logger; import org.slf4j.LoggerFactory;public c…