博客
关于我
Qt高级——Qt元对象系统源码解析
阅读量:794 次
发布时间:2023-03-03

本文共 3343 字,大约阅读时间需要 11 分钟。

Qt高级——Qt元对象系统源码解析

一、Qt元对象系统简介

1.1 元对象系统简介

Qt的信号槽和属性系统基于在运行时进行内省的能力,所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力。C++的内省比较有限,仅支持型别内省,C++的型别内省是通过运行时类型识别(RTTI)中的typeiddynamic_cast关键字来实现的。Qt拓展了C++的内省机制,但并没有采用C++的RTTI,而是提供了更为强大的元对象(Meta Object)机制,来实现内省机制。基于内省机制,可以列出对象的方法和属性列表,并且能够获取有关对象的所有信息,如参数类型。如果没有内省机制,QtScript和QML是难以实现的。

Qt中的元对象系统全称Meta Object System,是一个基于标准C++的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。元对象系统基于QObject类Q_OBJECT宏、元对象编译器MOC实现。

1.2 QObject类

QObject是每一个需要利用元对象系统的类的基类。任何从QObject继承的类都可以利用元对象系统提供的功能。

1.3 Q_OBJECT宏

Q_OBJECT宏定义在每一个类的私有数据段,用来启用元对象功能,比如动态属性、信号和槽。在一个QObject类或者其派生类中,如果没有声明Q_OBJECT宏,那么类的metaObject()返回的就是其父类的metaObject(),导致的后果是从类的实例获得的元数据其实都是父类的数据。因此,任何从QObject继承出来的类,无论是否定义声明了信号、槽和属性,都应该声明Q_OBJECT宏。

1.4 元对象编译器MOC

MOC(Meta Object Complier,元对象编译器)分析C++源文件,如果发现一个头文件中包含Q_OBJECT宏定义,会动态生成一个moc_xxxx命名的C++源文件,包含Q_OBJECT的实现代码,会被编译、链接到类的二进制代码中,作为类的完整的一部分。

二、Qt元对象系统源码解析

2.1 Q_OBJECT宏的定义

Q_OBJECT宏定义在src/corelib/kernel/Qobjectdefs.h文件中。任何从QObject派生的类都包含自己的元数据模型,一般通过宏Q_OBJECT定义。

2.2 QMetaObject类型

QMetaObject类定义在src/corelib/kernel/Qobjectdefs.h文件。QMetaObject中有一个嵌套结构封装了所有的数据:

  • superdata:元数据代表的类的基类的元数据
  • stringdata:元数据的签名标记
  • data:元数据的索引数组的指针
  • extradata:扩展元数据表的指针,指向QMetaObjectExtraData数据结构

2.3 QT_TR_FUNCTIONS宏定义

QT_TR_FUNCTIONS宏定义与翻译相关。宏定义如下:

static inline QString tr(const char *s, const char *c = 0) {
return staticMetaObject.tr(s, c);
}

这个宏定义用于翻译字符串,使用静态方法tr()来实现。

2.4 Qt中其它宏的定义

Qtsrc/corelib/kernel/Qobjectdefs.h文件中定义了大量的宏。这些宏大部分用于MOC工具解析和处理,具体包括:

  • Q_SLOTSQ_SIGNALSQ_PRIVATE_SLOTQ_EMIT等用于信号和槽的定义
  • Q_CLASSINFOQ_INTERFACESQ_PROPERTYQ_PRIVATE_PROPERTYQ_REVISIONQ_ENUMSQ_FLAGSQ_SCRIPTABLEQ_INVOKABLEQ_SIGNALQ_SLOT等用于元数据的定义和扩展

这些宏定义无实际的代码扩展作用,都是为了MOC工具解析和处理。

2.5 MOC的使用

在工程的Makefile文件中可以查找到MOC生成moc_xxx.cpp文件的命令。例如:

moc Object.cpp -o moc_Object.cpp

MOC工具根据类的元数据定义,自动生成相关的代码。

三、元对象编译器MOC

3.1 MOC功能

MOC主要功能包括:

  • 处理Q_OBJECT宏和signals/slots关键字,生成信号和槽的底层代码
  • 处理Q_PROPERTY()Q_ENUM(),生成属性系统和枚举类型的代码
  • 处理Q_FLAGS()Q_CLASSINFO(),生成额外的类元数据信息
  • 不需要MOC处理的代码可以用预定义的宏括起来,例如:
    #ifndef Q_MOC_RUN
    // 不需要MOC处理的代码
    #endif

3.2 MOC限制

MOC有一些限制:

  • 模板类不能使用信号槽机制
  • MOC不扩展宏,信号槽的定义不能使用宏,包括connect时的信号和槽名称和参数
  • 从多个类派生时,QObject(或其子类)作为多重继承的父类时,必须放在第一个
  • 函数指针不能作为信号或槽的参数
  • 用枚举类型或typedef的类型做信号和槽的参数时,必须使用完全限定语法
  • 信号和槽不能返回引用类型
  • 信号槽区域只能放置信号和槽的定义,不能放置其他代码或友元声明
  • 嵌套类不能含有信号槽

3.3 自定义类型的注册

在Qt中,自定义类型如果直接使用信号槽来传递,会产生错误:

QObject::connect: Cannot queue arguments of type 'XXXXX' (Make sure 'XXXXX' is registered using qRegisterMetaType())

原因是信号参数需要被拷贝存储在队列中,Qt需要知道如何处理这些类型。解决方法是:

  • 在类的顶部包含#include <QMetaType>
  • 类型定义完成后,加入声明:Q_DECLARE_METATYPE(XXXXX)
  • main()函数中注册类型:qRegisterMetaType<XXXXX>("XXXXX")
  • 如果需要传递引用类型,也要注册:qRegisterMetaType<XXXXX>("XXXXX&")
  • 3.4 MOC的使用示例

    Main.cpp中,可以看到MOC生成的代码:

    #include 
    #include "Object.h"
    int main(int argc, char *argv) {
    QCoreApplication a(argc, argv);
    Object ob("object");
    ob.setProperty("age", QVariant(30));
    qDebug() << "age: " << ob.age();
    qDebug() << "property age: " << ob.property("age").toInt();
    ob.setProperty("score", QVariant(90));
    qDebug() << "score: " << ob.score();
    qDebug() << "property score: " << ob.property("score").toInt();
    // 内省
    qDebug() << "object name: " << ob.objectName();
    qDebug() << "class name: " << ob.metaObject()->className();
    qDebug() << "isWidgetType: " << ob.isWidgetType();
    qDebug() << "inherit: " << ob.inherits("QObject");
    return a.exec();
    }

    通过上述步骤,可以看出MOC生成的代码包含了元对象系统的实现细节。

    转载地址:http://iuxfk.baihongyu.com/

    你可能感兴趣的文章
    PHP引入了泛型和集合两大重要特性,大大改善 PHP 代码的可维护性和可读性
    查看>>
    PHP引擎php.ini参数优化
    查看>>
    PHP引用(&)使用详解
    查看>>
    php引用及垃圾回收
    查看>>
    php当前时间的集中写法
    查看>>
    php循环比较数组中的值,如何从PHP数组中计算值并在foreach循环中仅显示一次值?...
    查看>>
    php微信 开发笔记,微信WebApp开发总结笔记
    查看>>
    php微信公众号开发access_token获取
    查看>>
    php微信公众号开发微信认证开发者
    查看>>
    php微信公众号开发用户基本信息
    查看>>
    php怎么将对象变成数组,php怎么将对象转换成数组
    查看>>
    RabbitMQ - 消息堆积问题的最佳解决方案?惰性队列
    查看>>
    php怎样比较两数大小,jquery如何判断两个数值的大小
    查看>>
    PHP性能监控 - 开启xhprof(一)
    查看>>
    PHP性能监控 - 怎么看xhprof报告(二)
    查看>>
    php截取字符串代码,PHP字符串截取_php
    查看>>
    php截取字符串,无乱码
    查看>>
    php手冊,php手冊之變量范圍
    查看>>
    PHP手机号码归属地查询API接口
    查看>>
    PHP执行耗时脚本实时输出内容
    查看>>