Qt教程(2) : Qt元对象系统

2023-11-01

​    元对象是指用于描述另一个对象结构的对象。使用编程语言具体实现时,其实就是一个类的对象,只不过这个对象专门用于描述另一个对象而已,比如 class B{…}; class A{…B mb;…};假设 mb 是用来描述类 A 创建的对象的,则 mb 就是元对象。

一、元对象系统

    Qt 的元对象系统提供的功能有:对象间通信的信号和槽机制、运行时类型信息和动态属性系统等。 

    元对象系统是 Qt 对原有的 C++进行的一些扩展,主要是为实现信号和槽机制而引入的,信号和槽机制是 Qt 的核心特征。

    要使用元对象系统的功能,需要满足以下三个条件

  • 该类必须继承自 QObject 类。

  • 必须在类声明的私有区域添加 Q_OBJECT 宏,该宏用于启动元对象特性,然后便可使用动态特性、信号和槽等功能了。

  • 元对象编译器(moc)为每个 QObject 的子类,提供实现了元对象特性所必须的代码。

    元对象系统具体运行原则

  • 因为元对象系统是对 C++的扩展,因此使用传统的编译器是不能直接编译启用了元对象系统的 Qt 程序的,对此在编译 Qt 程序之前,需要把扩展的语法去掉,该功能就是 moc 要做的事。 

  • moc 全称是 Meta-Object Compiler(元对象编译器),它是一个工具(类似于 qmake),该工具读取并分析 C++源文件,若发现一个或多个包含了 Q_OBJECT 宏的类的声明,则会生成另外一个包含了 Q_OBJECT 宏实现代码的 C++源文件(该源文件通常名称为 moc_*.cpp) ,这个新的源文件要么被#include 包含到类的源文件中,要么被编译链接到类的实现中(通常是使用的此种方法)。注意:新文件不会“替换”掉旧的文件,而是与原文件一起编译。 

二、Q_OBJECT

    Q_OBJECT源码如下:

/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_TR_FUNCTIONS \
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")
​
/* qmake ignore Q_OBJECT */
#define Q_OBJECT_FAKE Q_OBJECT QT_ANNOTATE_CLASS(qt_fake, "")
​
#ifndef QT_NO_META_MACROS
/* qmake ignore Q_GADGET */
#define Q_GADGET \
public: \
    static const QMetaObject staticMetaObject; \
    void qt_check_for_QGADGET_macro(); \
    typedef void QtGadgetHelper; \
private: \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    QT_ANNOTATE_CLASS(qt_qgadget, "") \
    /*end*/

    Q_OBJECT宏可以告诉预编译器该类具有gui元素,并且需要通过“ moc”运行。

    此时 moc 工具是通过 Qt Creator 来使用的,因此必须保证 moc 能发现并处理项目中包含有 Q_OBJECT 宏的类,为此,需要遵守以下规则

  1. 从 QObject 派生的含有 Q_OBJECT 宏的类的定义必须在头文件中。

  2. 确保 pro 文件中,是否列举了项目中的所有源文件(SOURCES 变量)和头文件(HEADERS 变量)

  3. 应在头文件中使用逻辑指令(比如#ifndef)防止头文件被包含多次。

  4. QObject 类应是基类列表中的第一个类。

  5. 由以上规则可见,使用 Qt Creator 编写代码时,类应定义在头文件中,成员函数的定义应位于源文件中(这样可避免头文件被包含多次产生的重定义错误),虽然这样编写程序比较麻烦,但这是一种良好的代码组织方式。

    不按规则 1 的方法编写程序,则 moc 工具就不能正确生成代码,这时的错误原因通常是未定义由 Q_OBJECT 展开后在类中声明的虚函数引起的,其错误信息如下:

    若使用 MinGw 编译,产生的错误信息类似如下:

undefined reference to `vtable for A'
//表示类 A 的虚函数表(vtable)不能正常生成,通常是有虚函数未定义。

    若使用 VC++2015 编译,产生的错误信息类似如下:

LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * __thiscall A::metaObject(void)const "
// 表示虚函数 A::metaObject 未定义。

    若定义了QObject 类的派生类,并进行了构建,在这之后再添加 Q_OBJECT 宏,则此时必须执行一次 qmake 命令(“构建”>“执行 qmake”),否则 moc 不能生成代码。

三、反射机制

    reflection 模式(反射模式或反射机制):是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。

  1. 元对象系统提供的功能之一是为 QObject 派生类对象提供运行时的类型信息及数据成员的当前值等信息,也就是说,在运行阶段,程序可以获取 QObject 派生类对象所属类的名称、父类名称、该对象的成员函数、枚举类型、数据成员等信息,其实这就是反射机制。

  2. 因为 Qt 的元对象系统必须从 QObject 继承,又从反射机制的主要作用可看到,Qt 的元对象系统主要是为程序提供了 QObject 类对象及其派生类对象的信息,也就是说不是从 QObject 派生的类对象,则无法使用 Qt 的元对象系统来获取这些信息。

3.1 Qt 具体实现反射机制的方法

  • Qt 使用了一系列的类来实现反射机制,这些类对对象的各个方面进行了描述,其中QMetaObject 类描述了 QObject 及其派生类对象的所有元信息,该类是 Qt 元对象系统的核心类,通过该类的成员函数可以获取 QObject 及其派生类对象的所有元信息,因此可以说 QMetaObject 类的对象是 Qt 中的元对象。注意:要调用 QMetaObject 类中的成员函数需要使用 QMetaObject 类型的对象。

  • 对对象的成员进行描述:一个对象包含数据成员、函数成员、构造函数、枚举成员等成员,在 Qt 中,这些成员分别使用了不同的类对其进行描述,比如函数成员使用类QMetaMethod 进行描述,属性使用 QMetaProperty 类进行描述等,然后使用QMetaObject 类对整个类对象进行描述,比如要获取成员函数的函数名,其代码如下:

QMetaMethod qm = metaObject->method(1); //获取索引为 1 的成员函数
qDebug()<<qm.name()<<"\n"; //输出该成员函数的名称。

3.2 使用 Qt 反射机制的条件

  • 需要继承自 QObject 类,并需要在类之中加入 Q_OBJECT 宏。

  • 注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入QObject::Q_INVOKABLE 宏。

  • 注册成员变量:若希望成员变量能被反射,需要使用 Q_PROPERTY 宏。

3.3 Qt 反射机制实现原理简述

    Q_OBJECT 宏展开之后有一个虚拟成员函数 meteObject(),该函数会返回一个指向QMetaObject 类型的指针,其原型为

virtual const QMetaObject *metaObject() const;

    因为启动了元对象系统的类都包含 Q_OBJECT 宏,所以这些类都有含有 metaObject()虚拟成员函数,通过该函数返回的指针调用 QMetaObject 类中的成员函数,便可查询到 QObject 及其派生类对象的各种信息。

    Qt 的 moc 会完成以下工作

  • 为 Q_OBJECT 宏展开后所声明的成员函数的成生实现代码

  • 识别 Qt 中特殊的关键字及宏,比如识别出 Q_PROPERTY 宏、Q_INVOKABLE宏、slot、signals 等

3.4 使用反射机制获取与类相关的信息

    QMetaObject 类中获取与类相关的信息的成员函数有

const char* className() const;

    获取类的名称,注意,若某个 QObject 的子类未启动元对象系统(即未使用 Q_OBJECT宏),则该函数将获取与该类最接近的启动了元对象系统的父类的名称,而不再返回该类的名称,因此建议所有的 QObject 子类都使用 Q_OBJECT 宏。

const QMetaObject* superClass() const;

    返回父类的元对象,若没有这样的对象则返回 0。

bool inherits(const QMetaObject* mo) const;

    若该类继承自 mo 描述的类型,则返回 true,否则返回 false。类被认为继承自身。

    QObject 类中获取与类相关的信息的成员函数有

bool inherits(const char* className) const;

    若该类是className 指定的类的子类则返回true,否则返回false。类被认为继承自身。

    以下是一个示例:

//头文件 m.h 的内容 
#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
class A:public QObject{ Q_OBJECT};
class B:public A{ Q_OBJECT};
class C:public QObject{Q_OBJECT};
class D:public C{};
#endif // M_H
​
//源文件 m.cpp 的内容 
#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
int main()
{
  A ma; B mb; C mc; D md;
  const QMetaObject *pa=ma.metaObject();
  const QMetaObject *pb=mb.metaObject();
  cout<<pa->className()<<endl; //输出类名 A
​
  //使用 QMetaObject::inherits()函数判断继承关系。
  cout<<pa->inherits(pa)<<endl; //输出 1,类被认为是自身的子类
  cout<<pa->inherits(pb)<<endl; //输出 0,由 pb 所描述的类 B 不是类 A 的子类。
  cout<<pb->inherits(pa)<<endl; //输出 1,由 pa 所描述的类 A 是类 B 的子类。
​
  //使用 QObject::inherits()函数判断继承关系。
  cout<<ma.inherits("B")<<endl; //输出 0,类 A 不是类 B 的子类。
  cout<<ma.inherits("A")<<endl; //输出 1,类被认为是自身的子类
  cout<<md.inherits("D")<<endl; //输出 0,因为类 D 未启动元对象系统。
  cout<<md.inherits("C")<<endl; //输出 1,虽然类 D 未启动元对象系统,但类 C 已启动,此种情形下能正确判断继承关系。
  cout<<md.metaObject()->className()<<endl; /输出 C,此处未输出 D,因为类 D 未启动元对象系统,与类 D 最接近的启动了元对象系统的父类是 C,因此返回 C。
​
  return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt教程(2) : Qt元对象系统 的相关文章

  • QWebSocketServer - 不释放内存

    首先 我在安全 websocket 服务器应用程序上运行 valgrind 并发现了一个问题 在 Qt Memcheck 中我必须检查 外部错误 看到它 一些字节是肯定输了 指着我的main就在我的地方QCoreApplication ex
  • 如何从 Qt 应用程序通过 ODBC 连接到 MySQL 数据库?

    我有一个新安装的 MySQL 服务器 它监听 localhost 3306 从 Qt 应用程序连接到它的正确方法是什么 原来我需要将MySQL添加到ODBC数据源 我在遵循这个视频教程后做到了这一点 https youtu be K3GZi
  • 选择合适的IDE

    您会推荐使用以下哪种 IDE 语言来在 Windows 下开发涉及识别手势并与操作系统交互的项目 我将使用 OpenCV 库来执行图像处理任务 之后 我将使用 win32 API 或 NET 框架与操作系统交互 具体取决于您建议的工具 性能
  • Qt 创建者 4.8.4。 Windows 7 - 64 位安装

    我在 QT 方面遇到了一些困难 我需要在学校使用它来完成一些 GUI 应用程序 我最近完成了以下步骤 1 mingw get inst 20120426 exefrom 来源锻造 http sourceforge net projects
  • 如何将flex和bison与Qt项目集成?

    我正在 git 源代码控制下使用 Qt4 制作 GUI 程序 Github页面 https github com vinayak garg dic sim 项目的一小部分需要扫描和解析 所以我想在项目中使用flex和bison 我能想到3种
  • 如何在不同的QT线程中创建一个窗口?

    我有一个应用程序 其中每个线程 主线程除外 都需要创建自己的窗口 我尝试创建一个线程然后调用this gt exec in the run功能 然而 在我接到那个电话之前我就收到了一个错误 ASSERT failure in QWidget
  • QMessageBox::about 可以根据标题长度调整大小吗?

    我想创建一个简单的 关于 对话框 但注意到QMessageBox about不会根据标题的长度调整其大小 由于字体较大 标题通常会更长 至少在我的桌面环境中 仅根据内容调整其大小 有没有办法确保对话框足够大以显示所有标题 我当然可以向 ab
  • C++ QT libXL 错误:“启动期间程序退出,代码为 0xc0000135”

    我正在尝试编写一个使用 libXL 的 QT 应用程序 但是当我尝试编译时 我收到一个弹出框 显示 During Startup program exited with code 0xc0000135 我已经准确地找出了哪一行导致了问题 它
  • MapItemView 在 dataChanged 信号后不会更新

    我正在使用 QMLMapItemView使用 C 的组件QAbstractListModel基于模型 这MapItemView当模型重置时 或者每当添加新项目或删除现有项目时 工作正常 但是 那MapItemView不反映对已添加项目的更改
  • 在 QML 中控制纹理 3D 对象的不透明度

    我对 QML 中的 Qt 3D 有点陌生 我正在尝试控制 Qt 3D 的不透明度textured3D 对象 我正在使用简单qml3d https github com tripolskypetr simpleqml3d测试项目来做到这一点
  • Qt - QProcess 不工作

    我尝试启动 Internet Explorer 所以我使用下面的代码 QProcess process new QProcess this QString temp C Program Files Internet Explorer iex
  • PySide6.1 与 matplotlib 3.4 不兼容

    当我只安装PySide6时 GUI程序运行良好 但是一旦我安装了matplotlib及其依赖包 包括pyqt5 则GUI程序将无法运行并输出以下错误消息 This application failed to start because no
  • Qt:关闭期间线程仍在运行时 qthread 被销毁

    我有一堂课 class centralDataPool public QObject Q OBJECT public centralDataPool QObject parent 0 centralDataPool commMonitor
  • 仅当从 Qt 连接时网页返回 HTTP 406 错误

    我有一个测试页面设置http mlecturedownload com test qt php http mlecturedownload com test qt php有以下代码
  • Qt QML MenuItem iconSource不显示

    我有一个非常简单的设置只是为了说明问题 import QtQuick Controls 1 4 import QtQuick Window 2 2 ApplicationWindow visible true width 640 heigh
  • PyQt:使用 alpha 通道创建 QPixmap,而不是预乘颜色通道

    我想创建一个 QPixmap 来使用 QPainter 进行绘制 QPixmap 应支持透明度 而不使用预乘颜色通道 目前 我通过创建具有所需尺寸的 QPixmap 并用每个通道 包括 alpha 设置为零的 QColor 填充它来实现此目
  • 使用样式表时的 QTabWidget tabPosition

    我目前正在使用样式表来设计应用程序的主题 这是我用于 QTabWidget 的样式表 QTabBar et QTabWidget QTabBar tab background qlineargradient spread pad x1 0
  • 如何从 ffmpeg 中打开的文件获取流信息?

    我正在尝试使用 ffmpeg 读取视频文件 我有与其旧版本相对应的工作代码 并开始尝试升级到最新的构建版本 将所有这些已弃用的函数替换为其实际的类似函数 但是我遇到了问题 似乎没有检索到任何流 并且视频负载停止在轨道中 这是我正在使用的代码
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • QListWidget 拖放项目从 Symbian 列表中消失

    我在实现带有可通过拖放重新排序的自定义项的 QListWidget 时遇到问题 问题是当我在某个项目上快速双击 非常短的拖放 时 该项目有时会从 QListWidget 中消失 这是我的小部件的构造函数 ListPopisiDragDrop

随机推荐

  • Qt多进程开发

    一 概述 随着客户端不断增加UI页面 运行程序的 体积 越来越大 初始化这些页面的时间也越来越长 1 单个进程架构的 瓶颈 在正式介绍Qt多进程架构开发前 先看看单个进程架构下客户端开发所遇到的 瓶颈 在单个进程的架构中 为了避免初始化某个
  • Python报错UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte

    Python报错UnicodeDecodeError gbk codec can t decode byte 这个错误是做NLP的小伙伴常见的一个错误 报错原因是读取的文件中有中文 核心思路 将 with open file as f 改成
  • 模型集成

    集成学习 ensemble learning 是机器学习中一类学习算法 值训练多个学习器并将它们组合起来使用的方法 这类算法通常在实践中会取得比单个学习器更好的预测结果 基于数据的集成 在训练阶段的数据扩充在测试阶段仍然适用 诸如图像多尺度
  • Linux find指令过滤掉没有查看权限的文件

    find 路径 name 文件名 2 gt dev null
  • 循环神经网络(RNN)实现股票预测

    活动地址 CSDN21天学习挑战赛 前言 1 什么是循环神经网络 一个最简单的循环神经网络如下图所示 这样的神经网络一共有3层 分别是输入层x 隐藏层h和输出层y 定义每一层的节点下标如下 k表示的是输出层的节点下标 j表示的是当前时间节点
  • HTML meta viewport属性说明(mark)

    点击打开原文 什么是Viewport 手机浏览器是把页面放在一个虚拟的 窗口 viewport 中 通常这个虚拟的 窗口 viewport 比屏幕宽 这样就不用把每个网页挤到很小的窗口中 这样会破坏没有针对手机浏览器优化的网页的布局 用户可
  • 【Linux C编程】学生信息管理系统--简易版

    学生信息管理系统 流程图 一 定义一个枚举体做菜单目录 二 定义两个结构体 三 定义一个菜单函数 四 添加 删除 修改 搜索 排序相关代码 五 主函数 完整代码 总结 此学生管理系统是本人用Linux系统下vim编辑器编写的 该学生管理系统
  • 树莓派使用 OLED 屏显示图片及文字

    树莓派默认是不带显示屏的 如果想要查看系统的一些信息 需要使用电脑登录到树莓派 或者通过 HDMI 连接外接显示器查看 这样做总是有点麻烦 我们可以通过外接一个 OLED 屏来显示一些关键参数或者图片 本文将详细介绍操作方法 OLED 模组
  • BES平台(恒玄) ANC调试笔记

    一 前言 最近比较忙 昨天更新了EQ 调试模块 今天就趁热打铁把ANC部分也写下 主要说一些基于恒玄平台2500的ANC 环境搭配 软件设置 和 常见问题分析 个人见解 有不足之处 敬请锤教 二 环境搭配 此处引用BES 原厂ANC调试指南
  • 算力的计算公式

    算力 Computing Power 的计算公式可以根据不同情况而变化 以下是几种常见的计算算力的公式 FLOPS Floating Point Operations per Second 在浮点运算的场景下 算力可以使用FLOPS来衡量
  • chrome浏览器美化插件:让您的浏览器页面冒水泡, 游小鱼儿

    下载插件和效果图 这是一个让你的浏览器冒泡泡的插件 浏览网页的时候仿佛置身于海底世界 插件下载地址 http files cnblogs com files diligenceday chromeExtension crx zip 效果图
  • Hystrix中线程上下文ThreadLocal

    ThreadLocal 在Java编程语言里ThreadLocal是用来方便开发人员在同一线程上下文中不同类 不同方法中共享信息的 ThreadLocal变量不受其他线程的影响 不同线程间相互隔离 也就是线程安全的 在实际的业务链路中从入口
  • 【考研经验】2019双非逆袭哈尔滨工业大学计算机经验分享

    转载于 王道论坛 原作者 南桥几经秋 20的学弟学妹们 我是2019哈工大深圳的双非小菜鸡 初试407 74 61 135 137 大家都知道是谁了吧 初试考的还行但是复试被虐的真的惨 还好初试分高稳住占了个哈深名额 所以劝双非的学弟学妹们
  • python常见图形代码可视化大全整理(包括动图)

    目录 一 离散型变量的可视化 1 饼图 1 1 matplotlib模块 1 2 panda模块 1 3 字母符合饼图 2 条形图 2 1 matplotlib模块 2 1 1 垂直或水平条形图 2 1 2 堆叠条形图 2 1 3 水平交错
  • 学习cocos2d-x之路(11)--JumpTo和JumpBy

    CCJumpTo和CCJumpBy属于延时动作 即经过一段时间才能完成的动作 CCJumpTo 把某一CCSprite跳到某一位置 CCJumpBy 把某一CCSprite跳起一段距离 它有一个方法reverse 它让对象按原路径返回 创建
  • 5. Spring Boot Security资源管理持久化的实现

    1 概述 本次给大家讲一下如何实现资源的持久化 2 表机构以及数据 insert into sys user demo user id user name user passwd values 1 admin admin insert in
  • android 开发技巧(6)--在 Canvas 上显示动画

    Android 中的Canvas 可以在屏幕上绘图 定义是这样的 可以把 Canvas 视为 Surface 的替身或者接口 图形便是绘制 在 Surface 上的 Canvas 封装了所有绘图调用 通过 Canvas 绘制 到 Surfa
  • Android 手游聚合SDK小知识(一)

    Android 手游聚合SDK小知识 一 Android 手游聚合SDK小知识 二 聚合分包 前言 回头想想 在安卓游戏SDK这个领域 我也呆了4年了 从啥都不懂的小菜鸟 逐渐靠自己不断学习 对这个行业也算有了一些理解 趁着最近有空 我想了
  • 【QT】QLabel三种设置文本的方法

    QLabel有三种设置文本的方法 掌握好Qt的属性系统 举一反三 可以做出很多效果 常规办法 ui gt label gt setText hello 取巧办法 ui gt label gt setProperty text hello 属
  • Qt教程(2) : Qt元对象系统

    元对象是指用于描述另一个对象结构的对象 使用编程语言具体实现时 其实就是一个类的对象 只不过这个对象专门用于描述另一个对象而已 比如 class B class A B mb 假设 mb 是用来描述类 A 创建的对象的 则 mb 就是元对象