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

news2025/1/10 23:43:41

描述

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_OBJECT
    Q_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.coloradmin.cn/o/1292406.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++STL的string类(一)

文章目录 前言C语言的字符串 stringstring类的常用接口string类的常见构造string (const string& str);string (const string& str, size_t pos, size_t len npos); capacitysize和lengthreserveresizeresize可以删除数据 modify尾插插入字符插入字符串 inserterasere…

解决WPS拖动整行的操作

如上图&#xff0c;想要把第4行的整行内容&#xff0c;平移到第1行。 1.选中第4行的整行 2.鼠标出现如图的样子时&#xff0c;按住鼠标左键&#xff0c;上移到第1行位置后&#xff0c;放开左键即可。

PPT设置章节

0 Preface/Foreward 1 添加章节方法 选择 > 开始 > 节 可以进行&#xff1a; 新增节重命名节删除所有节 相关节的内容如下&#xff1a;

vivado时序方法检查5

TIMING-14 &#xff1a; 时钟树上的 LUT 在时钟树上发现 LUT <cell_name> 。不建议在时钟路径上包含 LUT 单元。 描述 时钟路径上的 LUT 可能导致偏差过大 &#xff0c; 因为时钟必须在穿过互连结构的常规布线资源上进行布线。除偏差过大外 &#xff0c; 这些路径更…

【Java系列】函数式接口编程

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【算法每日一练]-结构优化(保姆级教程 篇4 树状数组,线段树,分块模板篇)

除了基础的前缀和&#xff0c;后面还有树状数组&#xff0c;线段树&#xff0c;分块的结构优化。 目录 分块 分块算法步骤&#xff1a; 树状数组 树状数组步骤&#xff1a; 线段树点更新 点更新步骤&#xff1a; 线段树区间更新 区间更新步骤&#xff1a; 分块 分块算…

Linux CentOS本地部署SQL Server数据库结合cpolar内网穿透实现公网访问

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装sql server二. 局域网测试连接三. 安装cpolar内网穿透四. 将sqlserver映射…

集合进阶指南:从基础知识到高级应用

集合高级 Collection集合 数组和集合的区别 相同点 都是容器,可以存储多个数据 不同点 数组的长度是不可变的,集合的长度是可变的数组可以存储基本数据类型和引用数据类型 集合只能存储引用数据类型,如果要存基本数据类型,需要存对应的包装类 集合类体系结构 Collectio…

一天一个设计模式---原型模式

基本概念 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;其主要目的是通过复制现有对象来创建新对象&#xff0c;而不是通过实例化类。原型模式允许在运行时动态创建对象&#xff0c;同时避免了耦合与子类化。 在原型模式中&#xff0…

Tap虚拟网卡

1 概述 Tap设备通常用于虚拟化场景下&#xff0c;其驱动代码位于drivers/net/tun.c&#xff0c;tap与tun复用大部分代码&#xff0c; 注&#xff1a;drivers/net/tap.c并不是tap设备的代码&#xff0c;而是macvtap和ipvtap&#xff1b; 下文中&#xff0c;我们统一称tap&#…

C++ vector基本操作

目录 一、介绍 二、定义 三、迭代器 四、容量操作 1、size 2、capacity 3、empty 4、resize 5、reserve 总结&#xff08;扩容机制&#xff09; 五、增删查改 1、push_back & pop_back 2、find 3、insert 4、erase 5、swap 6、operator[] 一、介绍 vector…

圣诞将至—C语言圣诞树代码来啦

文章目录 圣诞将至—C实现语言圣诞树源码 圣诞将至—C实现语言圣诞树 圣诞树 源码 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <math.h> #include <stdlib.h> #include <windows.h> #include <time.h> #define PI 3.14159265…

Maven-高效的Java项目构建与管理工具(含Maven详细安装与配置过程)

Maven 什么是Maven&#xff1f; 正如题目所说&#xff0c;Maven就是一款高效的Java项目构建与管理工具&#xff0c;基于项目对象模型&#xff08;POM&#xff09;概念&#xff0c;利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。是Apache软件基金会的一个开源…

全球市场调研:找准热门产品,开创跨境电商新蓝海

在全球数字化浪潮的推动下&#xff0c;跨境电商正蓬勃发展&#xff0c;成为连接世界各地消费者与商品的桥梁。然而&#xff0c;在竞争激烈的市场中要想脱颖而出&#xff0c;关键在于深入的全球市场调研。本文将探讨如何通过全球市场调研找准热门产品&#xff0c;开创跨境电商的…

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

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

基于OpenCV+CNN+IOT+微信小程序智能果实采摘指导系统——深度学习算法应用(含pytho、JS工程源码)+数据集+模型(四)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境Jupyter Notebook环境Pycharm 环境微信开发者工具OneNET云平台 模块实现1. 数据预处理2. 创建模型并编译3. 模型训练及保存1&#xff09;模型训练2&#xff09;模型保存 4. 上传结果1&#xff09;…

[TKDE2020]@Multi-Source_Spatial_Entity_Linkage

论文地址&#xff1a;https://arxiv.org/pdf/1911.09016v1.pdf&#xff08;下文中提及的引用信息如未解释&#xff0c;请索引原论文末的参考文献&#xff09; 论文中提到的SSTD2019Multi-Source Spatial Entity Linkage (提取码&#xff1a;i3xt) 论文重要部分翻译 Abstract …

LeedCode刷题---双指针问题(二)

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、盛水最多的容器 题目链接&#xff1a;盛最多水的容器 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xf…

C#大型LIS检验信息系统项目源码

LIS系统&#xff0c;一套医院检验科信息系统。它是以数据库为核心&#xff0c;将实验仪器与电脑连接成网&#xff0c;基础功能包括病人样本登录、实验数据存取、报告审核、打印分发等。除基础功能外&#xff0c;实验数据统计分析、质量控制管理、人员权限管理、试剂出入库等功能…

vscode eide arm-gcc 编译环境搭建调试

安装cube&#xff0c;vscode 1.安装vscode插件 C/C Extension Pack Chinese (Simplified) (简体中文) Language Pack Cortex-Debug Embedded IDE 工具链设置 2.软件工程生成 调试 3.生成工程&#xff0c;导入工程 4. 配置工程 编译完毕