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

Qt之元对象系统

Qt的元对象系统提供了信号和槽机制(用于对象间的通信)、运行时类型信息和动态属性系统。

元对象系统基于三个要素:

1、QObject类为那些可以利用元对象系统的对象提供了一个基类
2、在类声明中使用Q_OBJECT宏用于启用元对象特性,比如动态属性、信号和槽。
3、元对象编译器(moc)为每个QObject子类提供必要的代码来实现元对象特性。

moc工具读取C++源文件,如果发现一个或多个包含Q_OBJECT宏的类声明,它会生成另一个C++源文件,其中包含了这些类的每个元对象的代码。这个生成的源文件被#include进入类的源文件,更常见的是被编译并链接到类的实现中。

引入这个系统的主要原因是信号和槽机制,此外它还提供了一些额外功能:

1、QObject::metaObject() 返回与该类相关联的元对象。

2、QMetaObject::className() 在运行时以字符串形式返回类名,而无需通过 C++ 编译器提供本地运行时类型信息(RTTI)支持。

3、QObject::inherits() 函数返回一个对象是否是在 QObject 继承树内继承了指定类的实例。

4、QObject::tr() 和 QObject::trUtf8() 用于国际化的字符串翻译。

5、QObject::setProperty() 和 QObject::property() 动态地通过名称设置和获取属性。

6、QMetaObject::newInstance() 构造该类的新实例。

QMetaObject类包含有关Qt对象的元信息。每个在应用程序中使用的QObject子类都会创建一个QMetaObject实例,该实例存储了该QObject子类的所有元信息。此对象可通过QObject::metaObject()方法获得。
 

QMetaObject定义:

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject{//...struct { // private dataconst QMetaObject *superdata;const QByteArrayData *stringdata;const uint *data;typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);StaticMetacallFunction static_metacall;const QMetaObject * const *relatedMetaObjects;void *extradata; //reserved for future use} d;
}

QMetaObject是个结构体,没有构造函数。忽略掉所有方法声明,只剩一个结构体变量。

QMetaObject的变量会在moc_*.cpp文件中赋值:

//moc_mainwindow.cpp
QT_INIT_METAOBJECT const QMetaObject MainWindow::staticMetaObject = { {&QMainWindow::staticMetaObject,qt_meta_stringdata_MainWindow.data,qt_meta_data_MainWindow,qt_static_metacall,nullptr,nullptr
} };
变量名
const QMetaObject *superdata&QMainWindow::staticMetaObj
const QByteArrayData *stringdataqt_meta_stringdata_MainWin
const uint *dataqt_meta_data_MainWindow
StaticMetacallFunction static_metacallqt_static_metacall
const QMetaObject * const *relatedMetaObjectsnullptr
void *extradatanullptr

对于const QMetaObject *superdata = &QMainWindow::staticMetaObject;
MainWindow的staticMetaObject的superdata持有了QMainWindow的staticMetaObject,说明MainWindow可以访问QMainWindowstaticMetaObject

做个小测试:

//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{//...const QMetaObject *metaDta = staticMetaObject.d.superdata;while(metaDta){qDebug() << metaDta->className();metaDta = metaDta->d.superdata;}
}/*
输出结果:
QMainWindow
QWidget
QObject
*/

从这输出结果看,我们可以看出任何类的staticMetaObject都持有了父类的staticMetaObject

对于const QByteArrayData *stringdata = qt_meta_stringdata_MainWindow.data;

moc文件里找到qt_meta_stringdata_MainWindow变量:

//moc_mainwindow.cpp
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {{
QT_MOC_LITERAL(0, 0, 10) // "MainWindow"},"MainWindow"
};

qt_meta_stringdata_MainWindow是一个qt_meta_stringdata_MainWindow_t类型,这里对它进行了初始化。继续找到qt_meta_stringdata_MainWindow_t的定义:

//moc_mainwindow.cpp
struct qt_meta_stringdata_MainWindow_t {QByteArrayData data[1];char stringdata0[11];
};

也就是说stringdata的值为QT_MOC_LITERAL(0, 0, 10) // "MainWindow"

QT_MOC_LITERAL的定义:

//moc_mainwindow.cpp
#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)) \)

这个宏的作用是创建一个静态的 QByteArrayData 结构体,该结构体包含了字符串字面值的元数据。再结合注释我们推断stringdata代表"MainWindow"字符串,这里似乎是保存的类名MainWindow。从变量名qt_meta_stringdata_MainWindow推断,这个变量应该就是保存的元对象相关的字符串字面量,但我们默认工程没有元对象,我们在代码中加一个signal

//mainwindow.h
signals:void sigTest();

重新编译,可以看到,qt_meta_stringdata_MainWindow变量的初始化有所改变,从注释看明显包含了我们所加信号的名称:

//moc_mainwindow.cpp
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {{
QT_MOC_LITERAL(0, 0, 10), // "MainWindow"
QT_MOC_LITERAL(1, 11, 7), // "sigTest"
QT_MOC_LITERAL(2, 19, 0) // ""},"MainWindow\0sigTest\0"
};

对于const uint *data = qt_meta_data_MainWindow;

moc文件中找到qt_meta_data_MainWindow定义,它是一个uint数组,目前还看不出它的作用。

//moc_mainwindow.cpp
static const uint qt_meta_data_MainWindow[] = {// content:8,       // revision0,       // classname0,    0, // classinfo0,    0, // methods0,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags0,       // signalCount0        // eod
};

对于StaticMetacallFunction static_metacall = qt_static_metacall;

moc文件里找到qt_static_metacall定义,如果是默认工程,似乎也不做什么:

//moc_mainwindow.cpp
void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{Q_UNUSED(_o);Q_UNUSED(_id);Q_UNUSED(_c);Q_UNUSED(_a);
}

对于const QMetaObject * const *relatedMetaObjects = nullptr;void *extradata = nullptr;暂时不讨论。

我们目前找到了staticMetaObject初始化的位置,知道它被赋值了一些数据结构,这些数据结构都和moc相关。

我们看看QMetaObject的其他成员。

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject
{class Connection;//...
}class Q_CORE_EXPORT QMetaObject::Connection {//...
};

ConnectionQMetaObject的内部类,它代表了信号-槽的连接,那就是说我们平常使用的connect都和它相关,是个非常重要的角色。

我们可以看看我们一般使用的connect的定义:

//qobject.h
template <typename Func1, typename Func2>static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::typeconnect(/*...*/){//...return connectImpl(/*...*/);}

调用了connectImpl()

//qobject.h
static QMetaObject::Connection connectImpl(/*...*/);

的确是返回了QMetaObject::Connection,由此可见Connection是信号-槽系统的关键角色,它代表了一个建立的连接。

再看看其他接口:

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject
{//...//基本信息const char *className() const;const QMetaObject *superClass() const;bool inherits(const QMetaObject *metaObject) const Q_DECL_NOEXCEPT;//和类信息相关int classInfoOffset() const;int classInfoCount() const;int indexOfClassInfo(const char *name) const;QMetaClassInfo classInfo(int index) const;//和方法相关int methodOffset() const;int methodCount() const;int indexOfMethod(const char *method) const;QMetaMethod method(int index) const;//和枚举相关int enumeratorOffset() const;int enumeratorCount() const;int indexOfEnumerator(const char *name) const;QMetaEnum enumerator(int index) const;//和属性相关int propertyOffset() const;int propertyCount() const;int indexOfProperty(const char *name) const;QMetaProperty property(int index) const;QMetaProperty userProperty() const;//和构造器相关int constructorCount() const;int indexOfConstructor(const char *constructor) const;QMetaMethod constructor(int index) const;//和信号、槽相关int indexOfSignal(const char *signal) const;int indexOfSlot(const char *slot) const;static bool checkConnectArgs(const char *signal, const char *method);static bool checkConnectArgs(const QMetaMethod &signal,const QMetaMethod &method);static QByteArray normalizedSignature(const char *method);static QByteArray normalizedType(const char *type);//...
}

这些方法几乎提供了获取所有"元成员"信息的方式,包括构造器、方法、属性等,之所以说“元成员”,是因为被Q_INVOKABLEQ_PROPERTY等宏修饰的成员才具有"元能力"。

和信号-槽相关的接口:

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject
{// internal index-based connectstatic Connection connect(const QObject *sender, int signal_index,const QObject *receiver, int method_index,int type = 0, int *types = nullptr);// internal index-based disconnectstatic bool disconnect(const QObject *sender, int signal_index,const QObject *receiver, int method_index);//...// internal index-based signal activationstatic void activate(QObject *sender, int signal_index, void **argv);//...
}

从注释来看,这些接口用于内部,是以索引为基础的一些方法。

接下来是很多重载或者模板的invokeMethod()

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject
{//...invokeMethod(/*...*/);//...
}

用于调用obj的信号或者槽。

接下来是newInstance()

//qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject
{//...QObject *newInstance(/*...*/);//...
}

它是用来调用构造函数的。

觉得有帮助的话,打赏一下呗。。

           

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 计算机课程名,汇总
  • Windows系统网络配置命令详细指南
  • 编程题目积累(day5)
  • CSS技巧专栏:一日一例 2.纯CSS实现 多彩边框按钮特效
  • 296个地级市GDP相关数据(2000-2023年)
  • 右键连点器
  • 支持向量机 (support vector machine,SVM)
  • UML建模案例分析-类图中的关系
  • 大模型/NLP/算法面试题总结2——transformer流程//多头//clip//对比学习//对比学习损失函数
  • stm32使用双通道ADC读取
  • 2024辽宁省数学建模B题【钢铁产品质量优化】思路详解
  • TCP网络传输控制协议
  • 在 WebSocket 连接建立之前进行身份验证时,token 应该如何存储
  • 【ARM】MDK安装ARM_compiler5无法打开安装程序
  • Debezium系列之:验证mysql、mariadb等兼容mysql协议数据库账号权限
  • python3.6+scrapy+mysql 爬虫实战
  • $translatePartialLoader加载失败及解决方式
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【附node操作实例】redis简明入门系列—字符串类型
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • Angular 4.x 动态创建组件
  • Brief introduction of how to 'Call, Apply and Bind'
  • ES2017异步函数现已正式可用
  • ES6核心特性
  • Javascript设计模式学习之Observer(观察者)模式
  • mockjs让前端开发独立于后端
  • php ci框架整合银盛支付
  • V4L2视频输入框架概述
  • 从0实现一个tiny react(三)生命周期
  • 从零开始在ubuntu上搭建node开发环境
  • 动态规划入门(以爬楼梯为例)
  • 构建二叉树进行数值数组的去重及优化
  • 通过npm或yarn自动生成vue组件
  • 转载:[译] 内容加速黑科技趣谈
  • Python 之网络式编程
  • 阿里云移动端播放器高级功能介绍
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​queue --- 一个同步的队列类​
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • ###C语言程序设计-----C语言学习(3)#
  • #define,static,const,三种常量的区别
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (1)(1.13) SiK无线电高级配置(五)
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C语言)fread与fwrite详解
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (八)Flink Join 连接
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (四)模仿学习-完成后台管理页面查询
  • (一)Dubbo快速入门、介绍、使用
  • (转)http-server应用