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的元对象系统(Meta-Object System)是Qt框架的核心功能之一,为C语言增加了一些动态特性,借助元对象系统Qt可以实现以下功能 信号与槽机制(Signals and Slots)运行时类型信息(Run-Time Type In…...

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

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

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

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

React开发环境配置详细讲解-04
环境简介 前端随着规范化,可以说规范和环境插件配置满天飞,笔者最早接触的是jquery,那个开发非常简单,只要引入jquery就可以了,当时还写了一套UI框架,至今在做小型项目中还在使用,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】【结构型】【对比】| 主要区别包装的不同
👈️上一篇:享元模式 回 顾:结构型设计模式 1.代理模式👈️ 2.装饰器模式👈️ 3.适配器模式👈️ 4.组合模式👈️ 5.桥接模式👈️ 6.外观模式👈️ 7.享元模式&#x…...

信息学奥赛初赛天天练-17-阅读理解-浮点数精准输出与海伦公式的巧妙应用
PDF文档公众号回复关键字:20240531 1 2023 CSP-J 阅读程序1 阅读程序(程序输入不超过数组成字符串定义的范围:判断题正确填√,错误填;除特殊说明外,判断题1.5分,选择题3分,共计40分࿰…...

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

数据分析案例-在线食品订单数据可视化分析与建模分类
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...

构建LangChain应用程序的示例代码:2、使用LangChain库实现的AutoGPT示例:查找马拉松获胜成绩
AutoGPT 示例:查找马拉松获胜成绩 实现 https://github.com/Significant-Gravitas/Auto-GPT,使用LangChain基础组件(大型语言模型(LLMs)、提示模板(PromptTemplates)、向量存储(VectorStores)、嵌入(Embeddings)、工具(Tools))。…...

代码随想录算法训练营第三十四 |● 1005.K次取反后最大化的数组和 ● 134. 加油站 ● 135. 分发糖果
今天的解析写在了代码注释中 1005.K次取反后最大化的数组和 讲解链接: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 信息安全技术 信息系统密码应用测评要求 编写背景 随着信息技术的飞速发展,信息系统在社会经济活动中扮演着越来越重要的角色。信息安全问题也随之成为社会关注的焦点。GB-T 43206-2023《信息安全技术 信息系统密码应用测评要求》是针对信息系统中…...

线程进阶-1 线程池
一.说一下线程池的执行原理 1.线程池的七大核心参数 (1)int corePoolSize:核心线程数。默认情况下核心线程会一直存活,当设置allowCoreThreadTimeout为true时,核心线程也会被超时回收。 (2)i…...

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

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

AIGC智能办公实战 课程,祝你事业新高度
在数字化时代,人工智能(AI)已经渗透到我们生活的方方面面,从智能家居到自动驾驶,从医疗诊断到金融分析,AI助手正在改变我们的工作方式和生活质量。那么,你是否想过自己也能从零开始,…...

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

MySQL增删查改初阶
目录 一,数据库操作 1.关键字 show 显示当前数据库有哪些:show databases; 2.创建数据库 3.选中数据库 4.删除数据库 二,表的操作,在选中数据库的基础之上 1.查看表的结构 2.创建表 3.查看当前选中的数据库中…...

IService 接口中定义的常用方法
文心一言生成 以下是一些 IService 接口中定义的常用方法(以你提供的 UserSQL 类为例,该类继承自 ServiceImpl,因此也会拥有这些方法): 插入(新增) boolean save(T entity): 插入一条记录&…...

api网关kong对高频的慢接口进行熔断
一、背景 在生产环境,后端服务的接口响应非常慢,是因为数据库未创建索引导致。 如果QPS低的时候,因为后端服务有6个高配置的节点,虽然接口慢,还未影响到服务的正常运行。 但是,当QPS很高的时候,…...

python作业:实现一个任务列表管理系统,使用到python类、对象、循环等知识
实现一个简单的任务列表管理系统,可以用于python学习的作业或者练习。系统的功能包括: 用户可以添加任务、查看任务列表、标记任务为已完成,以及删除任务。 代码如下: class Task: def __init__(self, name, completedFalse):…...

大宋咨询(深圳产品价格调查)如何开展电子商品渠道价格监测
开展电子商品渠道价格监测是当今电商时代的重要任务之一。随着电子商务的迅猛发展,电子商品的价格波动日益频繁,市场竞争也愈发激烈。为了解优化渠道管理策略,提升品牌竞争力,大宋咨询(深圳市场调查)受客户…...

py黑帽子学习笔记_web攻击
python网络库 py2的urllib2 py3好像把urllib2继承到了标准库urllib,直接用urllib就行,urllib2在urllib里都有对应的接口 py3的urllib get请求 post请求,和get不同的是,先把post请求数据和请求封装到request对象,再…...

MVC、MVP 和 MVVM 架构总结
MVC、MVP 和 MVVM 是常见的软件架构模式,主要用于组织应用程序的结构,特别是在用户界面和业务逻辑之间进行分离。以下是对它们的详细解释,包括它们的差异、优缺点。 MVC(Model-View-Controller) 结构 Model…...

C++ vector的使用和简单模拟实现(超级详细!!!)
目录 前言 1.STL是什么 2.vector使用 2.1 vector简介 2.2 常用接口函数 1. 构造函数 2.operator[ ]和size,push_back 3. 用迭代器进行访问和修改 4. 范围for遍历 5.修改类型函数 pop_back find insert erase 6. 容量相关函数capacity resize reserve 3.…...

MySQL中,不能在一个DML(数据操纵语言,如INSERT, UPDATE, DELETE)语句中直接引用目标表进行子查询
错误示例 <delete id"deleteOldRelations">DELETE FROM departments_closure_tableWHERE descendant IN ( SELECT descendant FROM departments_closure_tableWHERE ancestor #{departmentId})</delete>程序运行之后,会报错:You …...

【CH32V305FBP6】4. systick 配置
配置 main.c void SYSTICK_Init_Config(u_int64_t ticks) {SysTick->SR & ~(1 << 0);//clear State flagSysTick->CMP ticks - 1;SysTick->CNT 0;SysTick->CTLR 0xF;NVIC_SetPriority(SysTicK_IRQn, 15);NVIC_EnableIRQ(SysTicK_IRQn); }中断计数 …...

【PECL】在扩展中实现 autoload
【PECL】在扩展中实现 autoload 摘要PHP代码想这么写C 代码这么实现 摘要 php-8.3.x 用扩展写个框架。想实现类管理器,自动加载,上代码: PHP代码想这么写 $ws new \Ziima\Applet(); $ws->import(Ziima, ../base/core); $ws->runAu…...