宏Q_OBJECT中的数据部分已经在在上一篇https://blog.csdn.net/Master_Cui/article/details/109007524分析完了,但是,MOC文件中还有一部分函数也在MOC文件中实现

对应的函数如下

 virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \

除了上面的虚函数外,还有两个信号函数,由MOC实现

// SIGNAL 0
void moctest::sigf1(double _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };QMetaObject::activate(this, &staticMetaObject, 0, _a);
}// SIGNAL 1
int moctest::sigf2(char _t1, int _t2)
{int _t0{};void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0))), 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);return _t0;
}

两个信号函数代码都是先定义一个void*的指针数组,数组的第一个元素都是空,给元对系统内部注册元方法参数类型时使用,剩下的元素都是信号函数的参数的指针,然后将数组传入activate

virtual const QMetaObject *metaObject() const; 的实现

const QMetaObject *moctest::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

一般情况下,QObject::d_ptr->metaObject总为空,此时返回MOC文件中的数据staticMetaObject的地址,当使用qml编写界面程序时,会返回QObject::d_ptr->dynamicMetaObject()

virtual void *qt_metacast(const char *); 的实现

void *moctest::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_moctest.stringdata0))return static_cast<void*>(this);return QObject::qt_metacast(_clname);
}

这个函数首先判断字符串的指针是否为空,然后将字符串和MOC文件中表示类名的字符串进行比较,如果字符串内容是当前的类名,那么将当前对象的地址转为void*并返回,否则调用基类的qt_metacast,这个过程一直迭代到根上的基类 QObject::qt_metacast(_clname) 为止,如果 _clname 不在类的继承树上,那么返回值就是空。

virtual int qt_metacall(QMetaObject::Call, int, void **);的实现

int moctest::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QObject::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;
}

其中QMetaObject::Call是个枚举,在qobjectdefs.h中,枚举的定义如下:

enum Call {InvokeMetaMethod,ReadProperty,WriteProperty,ResetProperty,QueryPropertyDesignable,QueryPropertyScriptable,QueryPropertyStored,QueryPropertyEditable,QueryPropertyUser,CreateInstance,IndexOfMethod,RegisterPropertyMetaType,RegisterMethodArgumentMetaType};

InvokeMetaMethod 代表元方法调用,比如信号槽的调用。

然后从 ReadProperty 到 QueryPropertyUser ,是属性操作相关的。

CreateInstance 是用元构造函数生成新实例的调用方式。

IndexOfMethod qt_static_metacall()会根据这个调用方式,查询匹配的信号函数的相对序号。基类和当前类都有一大堆元方法,这些所有的元方法都有它的绝对序号和相对序号,绝对序号是从顶层基类 QObject 开始计数,相对序号从当前类开始计数。

RegisterPropertyMetaType是注册属性类型的调用方式

RegisterMethodArgumentMetaType是注册元方法参数类型的调用方式,与 qt_meta_data_moctest 数组元方法参数类型条目中信号槽参数(

// signals: parameters
QMetaType::Void, QMetaType::Double,    2,
QMetaType::Int, QMetaType::Char, QMetaType::Int,    2,    2,// slots: parametersQMetaType::Void, QMetaType::Double,    2,QMetaType::Int, QMetaType::Char,    2,QMetaType::Bool, QMetaType::Char,    2,

)对应。

对于信号槽的调用,_id 是元方法的绝对序号, _a 在信号函数里被封装,和信号函数中的_a对应

接着调用基类的qt_metacall

_id = QObject::qt_metacall(_c, _id, _a);

该方法的主要作用是重新获得信号槽的相对序号_id

然后

   if (_id < 0)return _id;//如果相对序号_id<0,说明被调用的信号槽不在当前的对象中,直接返回if (_c == QMetaObject::InvokeMetaMethod) {//要调用信号槽if (_id < 5)//被调用的信号槽在当前的对象中,调用qt_static_metacall对信号槽进行调用qt_static_metacall(this, _c, _id, _a);_id -= 5;//刷新id_} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {//如果是要对信号槽的参数进行匹配检查if (_id < 5)//被调用的信号槽在当前的对象中*reinterpret_cast<int*>(_a[0]) = -1;//将指针数组的第一个元素值为-1,表示匹配检查过了_id -= 5;}return _id;

所以,这个函数的作用就是:1、发起对信号槽函数的调用。2、对信号槽函数进行参数检查

static void qt_static_metacall的实现

void moctest::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<moctest *>(_o);//将_o转化为当前对象的指针Q_UNUSED(_t)switch (_id) {//然后根据相对序号_id调用具体的信号槽函数case 0: _t->sigf1((*reinterpret_cast< double(*)>(_a[1]))); break;case 1: { int _r = _t->sigf2((*reinterpret_cast< char(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2])));if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;//如果信号槽函数有返回值,将返回值保存到指针数组的第一个元素指向的对象中case 2: _t->slotf((*reinterpret_cast< double(*)>(_a[1]))); break;case 3: { int _r = _t->slotf2((*reinterpret_cast< char(*)>(_a[1])));if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); }  break;case 4: { bool _r = _t->slotf3((*reinterpret_cast< char(*)>(_a[1])));if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = std::move(_r); }  break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {//如果是信号序号的查询调用,在QT5中,connect前必须检查信号是否为函数指针,然后在做关联,然后在_a[0]中可以知道int *result = reinterpret_cast<int *>(_a[0]);//先取得指针数组第一个元素的地址{using _t = void (moctest::*)(double );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&moctest::sigf1)) {//判断信号函数的类型与_t是否一致*result = 0;//如果一致,将result指向的内容变为信号的相对序号return;}}{using _t = int (moctest::*)(char , int );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&moctest::sigf2)) {*result = 1;return;}}}
}

至此,MOC文件中的代码解析完毕,然后,信号槽得需要connect进行连接,后面会解析connect函数

参考

Qt5.14源码

https://qtguide.ustclug.org/

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

3.MOC文件解读(下)——MOC文件中的函数相关推荐

  1. webpack 读取文件夹下的文件_TypeScript完全解读(26课时)_1.TypeScript完全解读-开发环境搭建...

    1.TypeScript完全解读-开发环境搭建 初始化项目 手动创建文件夹 D:\MyDemos\tsDemo\client-demo 用VSCode打开 npm init:初始化项目 然后我们的项目 ...

  2. java中File类应用:遍历文件夹下所有文件

    练习: 要求指定文件夹下的所有文件,包括子文件夹下的文件 代码: package 遍历文件夹所有文件;import java.io.File;public class Test {public sta ...

  3. java读取文件夹下所有文件并替换文件每一行中指定的字符串

    java读取文件夹下所有文件并替换文件每一行中指定的字符串 该文章为转载文章,请尊重原文作者的劳动成果,好的文章应该被展示 原文传送门 多说两句,关键时候救人性命的人,可以称之为大人,随时做好利见大人 ...

  4. C#中获得文件夹下所有文件

    C#中获得文件夹下所有文件的两种方法 如果想要获得一个文件夹下所有的文件,对于大家来说应该很简单,此处也做了一下总结,归为两种方法, 方法一: 利用递归的方法获得所有文件 /// /// 存储文件的变 ...

  5. Java中怎么获取文件夹的名称_java获取文件夹下所有文件的名称

    <java获取文件夹下所有文件的名称>由会员分享,可在线阅读,更多相关<java获取文件夹下所有文件的名称(2页珍藏版)>请在人人文库网上搜索. 1.如果想要获得当前文件中的文 ...

  6. Matlab中对文件夹下.mat文件进行读取及操作

    Matlab读取文件夹中的.mat文件并加载进行信号重采样 文件夹中的数据读取 获取文件夹下文件的各项信息,主要是根据用户所需要的文件夹路径path及文件后缀,使用dir方法可得到文件夹下目标文件的各 ...

  7. python删除文件夹中的jpg_python删除文件夹下相同文件和无法打开的图片

    前天不小心把硬盘格式化了,丢了好多照片,后来用Recuva这款软件成功把文件恢复过来,可是恢复的文件中有好多重复的文件和无法打开的图片,所以写了两个python的小程序用来解决这个问题 删除相同文件: ...

  8. ubuntu | 命令行中输出文件夹下的文件+输出某个后缀的文件+文件名作为参数运行py脚本

    文章目录 1. 打印test_dir文件夹下所有文件名 2. 打印test_dir/*.txt的所有文件名 3. 打印文件夹下所有文件名称,不管后缀 4. 把文件名当做python脚本的入参运行脚本 ...

  9. python怎么读文件夹下的文件夹-python2.7读取文件夹下所有文件名称及内容的方法...

    最近稍稍有点空闲时间,于是重新温习了一下之前学习过的python基础.废话不多说,记录一下自己的所得. 首先,安装什么的不在本人的温习范围,另,本人使用的是windows下的python2.7版本,且 ...

  10. EVC实现拷贝文件夹下所有文件

    //======================================================================== //TITLE: //    EVC实现拷贝文件夹 ...

最新文章

  1. php保存base64数据
  2. python开发好学吗-Python是不是很难学?
  3. 在 Java 中应用设计模式 - Factory Method
  4. net类库中发送电子邮件的方法总结
  5. python列表解析式如何使用_python列表解析式,生成器,及部分内建函数使用方法...
  6. UltraEdit常用配置搭建Java/C开发环境
  7. 征战蓝桥 —— 2016年第七届 —— C/C++A组第8题——四平方和
  8. python连接mongo数据库
  9. 巨坑!同事离职前一天,偷偷将一个470欧的色环电阻串进我的电源线中......
  10. Microsoft REST API指南
  11. linux mysql服务基础操作(二)
  12. 计算机调剂到材料科学与工程,2019年郑州大学材料科学与工程专业考研调剂信息...
  13. java equals getclass_Java equals()方法 – 子类中equals的语义如何确定getClass和instanceof的使用...
  14. springboot项目打jar包部署到linux中
  15. 计算机网络实验一VLAN间路由
  16. 吉他音阶训练——问题解答 (三)
  17. 如何将CHM转换为PDF文件?
  18. (附源码)Python学生兼职平台系统 毕业设计 160938
  19. 【渝粤题库】陕西师范大学200461英语阅读(一) 作业(高起专、高起本)
  20. 90后小伙自己开发APP年赚10亿,自己开发APP需要什么工具

热门文章

  1. RPM快速打包2017-08-21
  2. 《MySQL技术内幕:InnoDB存储引擎第2版》——3.1 参数文件
  3. Btrace入门到熟练小工完全指南
  4. 秒杀系统架构解密与防刷设计 - 高可用架构系列
  5. 二分查找(递归与非递归)
  6. 【Based Android】Android Sensor感应器介绍(二)线程中刷新UI 创建一个android测力计...
  7. 都说 HashMap 是线程不安全的,到底体现在哪儿?
  8. 线性表的链式表示——循环链表
  9. LVS+Keepalived-DR模式负载均衡高可用集群
  10. Java开发微信公众号(四)---微信服务器post消息体的接收及消息的处理