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

Qt moc系统的黑魔法?

Qt的元对象系统(Meta-Object System)是Qt框架的核心功能之一,为C++语言增加了一些动态特性,借助元对象系统Qt可以实现以下功能

  • 信号与槽机制(Signals and Slots)
  • 运行时类型信息(Run-Time Type Information, RTTI)
  • 属性系统(Property System)
  • 动态对象创建(Dynamic Object Creation)
  • 动态方法调用(Dynamic Method Invocation)
  • 安全的类型转换(Dynamic Cast)

元对象系统的实现需要借助moc (Meta-Object_Compiler)工具, moc工具扫描包含Q_OBJECT宏的头文件,为为我们自动生成了元对象系统的实现代码,我们以一个简单的MainWindow例子来探究一下moc系统有什么黑魔法,示例完整代码放在文章最后。

QObject派生类的元信息编码和存储

元(Meta)在C++中是和编译期相关的概念,元对象用来存储一个类在编译器就确定的类型相关信息, 即每一个类的类型和方法信息都是编译期都是已经确定了的,在一般自己实现C++反射系统的方案中,存储类的元信息,无不例外的都是借助一个静态成员变量,由于静态变量是存储在静态存储区,生命周期伴随整个程序,并且其初始化是在程序加载后,main入口函数之前,所以用一个静态类来存储元信息便很自然了,如下,Qt中就是给每一个定义Q_OBJECT宏的类定义了一个static QMetaObject成员对象。

staticMetaObject这个静态成员的定义就在moc工具自动生成的moc_mainwindow.cpp文件之中,其中主要包含类的派生信息和信号与槽函数信息。

qt_meta_stringdata_MainWindow和qt_meta_data_MainWindow的定义在下面两张截图之中,我们先来看一下qt_meta_stringdata_MainWindow,这个结构里面将信号和槽函数的信息编码存储到了一个字符串之中,QT_MOC_LITERAL(0, 0, 10)中,第一个参数为编号,第二个为在下面字符串中的起始位置,第二个参数为函数名称的长度。

qt_meta_data_MainWindow这个结构指定了每个信号和槽函数在上面的“字符串”中的位置以及函数的返回值,参数类型信息,这两个结构就编码存储了信号槽函数的所有信息了。

槽函数的调用

这里先讨论一个问题,如果知道一个函数的字符串名称,怎么调用这个函数? 注意,这里的问题是,你现在只知道一个字符串, 虽然你知道它是那个函数,但是你怎么来调用?

std::string func_name = "my_slot";
//how to call my_slot() ?
func_name()?  // this is error~//the possiable way
if(func_name == "my_slot"){my_slot();
} else if( func_name = "my_slot1"){my_slot1();
}

在老版本的Qt中,connect链接信号和槽的时候,也是直接连接的函数字符串名称,通过字符串来调用函数,就不得不借用我们moc文件里面生成的qt_static_metacall了,这里的实现也是很明了,首先找到函数的索引,然后通过switch case进行函数调用。

void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<MainWindow *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->my_signal(); break;case 1: _t->my_signal_param((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2]))); break;case 2: _t->on_actionExit_triggered(); break;case 3: _t->my_slot(); break;case 4: _t->my_slot_param((*reinterpret_cast< int(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (MainWindow::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal)) {*result = 0;return;}}{using _t = void (MainWindow::*)(int , double );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal_param)) {*result = 1;return;}}}
}

信号函数的实现

我们在定义信号的时候,只给出了一个信号函数的申明,信号函数的实现也是moc自动生成的

// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

总结:

我们可以发现,QT的元对象系统并没有什么神奇之处,主要干了下面几件事情

  • 通过一个静态成员QMetaObject存储类的元信息
  • 通过一个字符串编码存储了信号和槽函数信息
  • 通过信号和槽函数的索引,在switch case中调用槽函数
  • 自动生成信号函数的实现代码

mainwindow示例:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);explicit MainWindow(QWidget* parent, int id);~MainWindow();void my_general_fun();private slots:void on_actionExit_triggered();void my_slot();void my_slot_param(int iiii);signals:void my_signal();void my_signal_param(int jjjj, double kkkk);private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

moc生成的实现代码:

/****************************************************************************
** Meta object code from reading C++ file 'mainwindow.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.15.2)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include <memory>
#include "../../../../mainwindow.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'mainwindow.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.15.2. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_MainWindow_t {
QByteArrayData data[10];
char stringdata0[99];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_MainWindow_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {
{
QT_MOC_LITERAL(0, 0, 10), // "MainWindow"
QT_MOC_LITERAL(1, 11, 9), // "my_signal"
QT_MOC_LITERAL(2, 21, 0), // ""
QT_MOC_LITERAL(3, 22, 15), // "my_signal_param"
QT_MOC_LITERAL(4, 38, 4), // "jjjj"
QT_MOC_LITERAL(5, 43, 4), // "kkkk"
QT_MOC_LITERAL(6, 48, 23), // "on_actionExit_triggered"
QT_MOC_LITERAL(7, 72, 7), // "my_slot"
QT_MOC_LITERAL(8, 80, 13), // "my_slot_param"
QT_MOC_LITERAL(9, 94, 4) // "iiii"},
"MainWindow\0my_signal\0\0my_signal_param\0"
"jjjj\0kkkk\0on_actionExit_triggered\0"
"my_slot\0my_slot_param\0iiii"
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_MainWindow[] = {
// content:
8,       // revision
0,       // classname
0,    0, // classinfo
5,   14, // methods
0,    0, // properties
0,    0, // enums/sets
0,    0, // constructors
0,       // flags
2,       // signalCount// signals: name, argc, parameters, tag, flags
1,    0,   39,    2, 0x06 /* Public */,
3,    2,   40,    2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags
6,    0,   45,    2, 0x08 /* Private */,
7,    0,   46,    2, 0x08 /* Private */,
8,    1,   47,    2, 0x08 /* Private */,// signals: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, QMetaType::Double,    4,    5,// slots: parameters
QMetaType::Void,
QMetaType::Void,
QMetaType::Void, QMetaType::Int,    9,0        // eod
};void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<MainWindow *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->my_signal(); break;case 1: _t->my_signal_param((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2]))); break;case 2: _t->on_actionExit_triggered(); break;case 3: _t->my_slot(); break;case 4: _t->my_slot_param((*reinterpret_cast< int(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (MainWindow::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal)) {*result = 0;return;}}{using _t = void (MainWindow::*)(int , double );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal_param)) {*result = 1;return;}}}
}QT_INIT_METAOBJECT const QMetaObject MainWindow::staticMetaObject = { {QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),qt_meta_stringdata_MainWindow.data,qt_meta_data_MainWindow,qt_static_metacall,nullptr,nullptr
} };const QMetaObject *MainWindow::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *MainWindow::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_MainWindow.stringdata0))return static_cast<void*>(this);return QMainWindow::qt_metacast(_clname);
}int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QMainWindow::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 5)qt_static_metacall(this, _c, _id, _a);_id -= 5;} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 5)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 5;}return _id;
}// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

相关文章:

Qt moc系统的黑魔法?

Qt的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的核心功能之一&#xff0c;为C语言增加了一些动态特性&#xff0c;借助元对象系统Qt可以实现以下功能 信号与槽机制&#xff08;Signals and Slots&#xff09;运行时类型信息&#xff08;Run-Time Type In…...

MyBatis开发中常用总结

文章目录 常用MyBatis参数映射单个参数多个参数使用索引【不推荐】Param注解Map传参POJO【推荐】List数组 动态标签\<if>标签\<trim>标签\<where>标签\<set>标签\<foreach>标签 MyBatis查询一对一一对多 常用MyBatis参数映射 单个参数 XML中可…...

Git基本使用教程(学习记录)

参考文章链接&#xff1a; Git教程&#xff08;超详细&#xff0c;一文秒懂&#xff09; RUNOOB Git教程 Git学习记录 1Git概述 1.1版本控制软件功能 版本管理&#xff1a;更新或回退到历史上任何版本&#xff0c;数据备份共享代码&#xff1a;团队间共享代码&#xff0c;…...

【Linux-RTC】

Linux-RTC ■ rtc_device 结构体■ RTC 时间查看与设置■ 1、时间 RTC 查看■ 2、设置 RTC 时间 ■ rtc_device 结构体 Linux 内核将 RTC 设备抽象为 rtc_device 结构体 rtc_device 结构体&#xff0c;此结构体定义在 include/linux/rtc.h 文件中 ■ RTC 时间查看与设置 ■ 1…...

机器学习目录

文章目录 基本概念有监督学习回归问题分类问题 无监督学习聚类问题异常检测 基本概念 pass 有监督学习 回归问题 通过拟合函数&#xff0c;解决连续值的预测问题梯度下降法优化&#xff1b;最小二乘法求解&#xff1b;度量指标 均方误差&#xff1b;均方根误差&#xff1b;平…...

React开发环境配置详细讲解-04

环境简介 前端随着规范化&#xff0c;可以说规范和环境插件配置满天飞&#xff0c;笔者最早接触的是jquery&#xff0c;那个开发非常简单&#xff0c;只要引入jquery就可以了&#xff0c;当时还写了一套UI框架&#xff0c;至今在做小型项目中还在使用&#xff0c;show一张效果…...

Go 如何通过 Kafka 客户端库 生产与消费消息

文章目录 0.前置说明1. confluent-kafka-go2. sarama3. segmentio/kafka-go4. franz-go选择建议 1.启动 kafka 集群2.安装 confluent-kafka-go 库3.创建生产者特殊文件说明如何查看.log文件内容 4.创建消费者 0.前置说明 Go 语言中有一些流行的 Kafka 客户端库。以下是几个常用…...

【设计模式深度剖析】【B】【结构型】【对比】| 主要区别包装的不同

&#x1f448;️上一篇:享元模式 回 顾&#xff1a;结构型设计模式 1.代理模式&#x1f448;️ 2.装饰器模式&#x1f448;️ 3.适配器模式&#x1f448;️ 4.组合模式&#x1f448;️ 5.桥接模式&#x1f448;️ 6.外观模式&#x1f448;️ 7.享元模式&#x…...

信息学奥赛初赛天天练-17-阅读理解-浮点数精准输出与海伦公式的巧妙应用

PDF文档公众号回复关键字:20240531 1 2023 CSP-J 阅读程序1 阅读程序&#xff08;程序输入不超过数组成字符串定义的范围&#xff1a;判断题正确填√&#xff0c;错误填&#xff1b;除特殊说明外&#xff0c;判断题1.5分&#xff0c;选择题3分&#xff0c;共计40分&#xff0…...

mysql - 为什么MySQL不建议使用NULL作为列默认值?

为什么MySQL不建议使用NULL作为列默认值&#xff1f; InnoDB有4中行格式&#xff1a; Redundant : 非紧凑格式,5.0 版本之前用的行格式,目前很少使用,Compact : 紧凑格式,5.1 版本之后默认行格式,可以存储更多的数据Dynamic , Compressed : 和Compact类似,5.7 版本之后默认使…...

数据分析案例-在线食品订单数据可视化分析与建模分类

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…...

构建LangChain应用程序的示例代码:2、使用LangChain库实现的AutoGPT示例:查找马拉松获胜成绩

AutoGPT 示例&#xff1a;查找马拉松获胜成绩 实现 https://github.com/Significant-Gravitas/Auto-GPT&#xff0c;使用LangChain基础组件&#xff08;大型语言模型(LLMs)、提示模板(PromptTemplates)、向量存储(VectorStores)、嵌入(Embeddings)、工具(Tools)&#xff09;。…...

代码随想录算法训练营第三十四 |● 1005.K次取反后最大化的数组和 ● 134. 加油站 ● 135. 分发糖果

今天的解析写在了代码注释中 1005.K次取反后最大化的数组和 讲解链接&#xff1a;https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%90%8E%E6%9C%80%E5%A4%A7%E5%8C%96%E7%9A%84%E6%95%B0%E7%BB%84%E5%92%8C.html class Solution { public:static bool cmp(i…...

GB-T 43206-2023 信息安全技术 信息系统密码应用测评要求

GB-T 43206-2023 信息安全技术 信息系统密码应用测评要求 编写背景 随着信息技术的飞速发展&#xff0c;信息系统在社会经济活动中扮演着越来越重要的角色。信息安全问题也随之成为社会关注的焦点。GB-T 43206-2023《信息安全技术 信息系统密码应用测评要求》是针对信息系统中…...

线程进阶-1 线程池

一.说一下线程池的执行原理 1.线程池的七大核心参数 &#xff08;1&#xff09;int corePoolSize&#xff1a;核心线程数。默认情况下核心线程会一直存活&#xff0c;当设置allowCoreThreadTimeout为true时&#xff0c;核心线程也会被超时回收。 &#xff08;2&#xff09;i…...

LabVIEW中PID控制器系统的噪声与扰动抑制策略

在LabVIEW中处理PID控制器系统中的噪声和外部扰动&#xff0c;需要从信号处理、控制算法优化、硬件滤波和系统设计四个角度入手。采用滤波技术、调节PID参数、增加前馈控制和实施硬件滤波器等方法&#xff0c;可以有效减少噪声和扰动对系统性能的影响&#xff0c;提高控制系统的…...

JavaWeb笔记整理+图解——Listener监听器

欢迎大家来到这一篇章——Listener监听器 监听器和过滤器都是JavaWeb服务器三大组件&#xff08;Servlet、监听器、过滤器&#xff09;之一&#xff0c;他们对于Web开发起到了不可缺少的作用。 ps&#xff1a;想要补充Java知识的同学们可以移步我已经完结的JavaSE笔记&#x…...

AIGC智能办公实战 课程,祝你事业新高度

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;从医疗诊断到金融分析&#xff0c;AI助手正在改变我们的工作方式和生活质量。那么&#xff0c;你是否想过自己也能从零开始&#xff0c;…...

专科生听劝 这种情况你就不要专转本了

罗翔老师说过&#xff0c;读书学习主要作用是提高人的下限 我们能掌握的只有学习&#xff0c;以确保学历不会太差再去等机遇让自己活得更好 大部分情况来说&#xff0c;专科生努力去专转本挺好的提升自己准没错&#xff0c;我当年也是一心这样想的&#xff0c;但今天不得不说点…...

MySQL增删查改初阶

目录 一&#xff0c;数据库操作 1.关键字 show 显示当前数据库有哪些&#xff1a;show databases&#xff1b; 2.创建数据库 3.选中数据库 4.删除数据库 二&#xff0c;表的操作&#xff0c;在选中数据库的基础之上 1.查看表的结构 2.创建表 3.查看当前选中的数据库中…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...