zoukankan      html  css  js  c++  java
  • 深入了解Qt(二)之元对象系统(Meta-Object System)

    深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解。在此向原作者表示感谢!

    Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象系统的机制,在这里就做一些补充。

    Meta Object System的设计基于以下几个基础设施:

    QObject类,作为每一个需要利用元对象系统的类的基类。也就是说只有继承QObject类才能使用MOS。

    Q_OBJECT宏,定义在每一个类的私有数据段,用来启用元对象功能,比如,动态属性、信号和槽。

    元对象编译器moc(Meta Object Compiler),如果一个头文件中包含Q_OBJECT宏定义,moc就会将该文件编译成C++源文件。该原文件包含了Q_OBJECT的实现代码,也被编译,最终链接到这个类的二进制代码中。因为它也是这个类的一部分。通常,这个新的C++原文件会再以前的C++原文件前面加上moc_作为新的文件名。

    如下图所示:

     

    除了提供在对象间通讯的机制外,元对象系统还包含以下几种功能:

    QObject::metaObject()方法,获得与一个类相关联的meta-object。

    QMetaObject::className()方法,在运行期间返回一个对象的类名,不需要本地C++编译器的RTTI(run time type information)支持。

    QObject::inherits()方法,用来判断一个对象的类是不是从一个特定的类继承而来。

    QObject::tr()、QObject::trUtf8(),为软件的国际化翻译字符串。

    QObject::setProperty()、QObject::property(),根据属性名动态的设置和获取属性值。

    使用qobject_cast()方法在QObject类之间提供动态转换,qobject_cast()方法的功能类似于标准C++的dynamic_cast(),但是qobject_cast()不需要RTTI的支持。在一个QObject类或者它的派生类中,如果不定义Q_OBJECT宏,那么这些功能将不能被使用。从meta-object的观点来看,一个没有定义Q_OBJECT宏的类与它最接近的那个祖先类是相同的。那么QMetaObject::className()方法返回的名字并不是该类的名字,而是与它最近接的那个祖先类的名字。所以,任何从QObject继承的类都必须定义Q_OBJECT宏。

    Meta Object的所有数据和方法都封装在QMetaObject类中,它包含一个Qt类的meta信息,并且提供查询功能。

    meta信息包含:

    1. 信号表(signal table),与对应Qt类相关的系统定义及自定义的signal的名字。
    2. 槽表(slot table),与对应Qt类相关的系统定义及自定义的slot的名字。
    3. 类信息表(class info table),Qt类的类型信息。
    4. 属性表(property table),与对应类中的所有属性的名字。
    5. 指向parent meta object的指针。

    请参考下图,Qt Meta Data Tables:


    Qt Meta Class与Qt Class之间的对应关系:

    Q_OBJECT宏的定义如下:

    #define Q_OBJECT   
    public:   
    Q_OBJECT_CHECK   
    static const QMetaObject staticMetaObject;   
    virtual const QMetaObject *metaObject() const;   
    virtual void *qt_metacast(const char *);   
    QT_TR_FUNCTIONS   
    virtual int qt_metacall(QMetaObject::Call, int, void **);   

    staticMetaObject是静态常量,metaObject()是获取该对象指针的方法。所有QObject的对象都会共享staticMetaObject变量,靠它完成所有信号和槽的功能。

    QMetaObject的定义如下:

    1. struct Q_CORE_EXPORT QMetaObject  
    2. {  
    3. // ......  
    4. struct { // private data  
    5. const QMetaObject *superdata;  
    6. const char *stringdata;  
    7. const uint *data;  
    8. const void *extradata;  
    9. } d;  
    10. };

    上面的代码就是QMetaObject类所定义的所有数据成员,就是这些数据成员记录了所有的signal、slot、property、class information信息。

    const QMetaObject *superdata,该变量指向与之对应的QObject类的父类对象,或者是祖先类的QMetaObject对象。每一个QObject类或者其派生类可能有一个父类或者父类的父类。那么superdata就是指向与其最接近的祖先类中的QMetaObject对象。如果没有父类,那么该变量就是一个NULL指针。

    举个例子:

    class Animal : public QObject{
    Q_OBJECT
    ...
    };
    
    class Cat : public Animal{
    Q_OBjECT
    ...
    };

    Cat::staticMetaObject.d.superdata这个指针指向的对象就是Animal::staticMetaObject,而Animal::staticMetaObject.d.superdata这个指针指向的对象就是QObject::staticMetaObject,QObject::staticMetaObject.d.superdata指针的值就是NULL。

    再例如下面的例子:

    class Animal : public QObject{
    ...
    };
    
    class Cat : public Animal{
    Q_OBjECT
    ...
    };

    那么,Cat::staticMetaObject.d.superdata 这个指针变量指向的对象是 QObject::staticMetaObject,因为 Animal::staticMetaObject 这个对象是不存在的。

    const char*stringdata,这是一个指向string data的指针。但它和我们平时所使用的一般的字符串指针却很不一样,我们平时使用的字符串指针只是指向一个字符串的指针,而这个指针却指向的是很多个字符串。那么它不就是字符串数组吗?哈哈,也不是。因为C++的字符串数组要求数组中的每一个字符串拥有相同的长度,这样才能组成一个数组。那它是不是一个字符串指针数组呢?也不是,那它到底是什么呢?让我们来看一看它的具体值,以QObject这个class的QMetaObject为例来说明。

    下面是QObject::staticMetaObject.d.stringdata指针所指向的多个字符串数组,其实它就是指向一个连续的内存区,而这个连续的内存区中保存了若干个字符串。

    static const char qt_meta_stringdata_QObject[] =  
    {  
    "QObjectdestroyed(QObject*)destroyed()"  
    "deleteLater()_q_reregisterTimers(void*)"  
    "QStringobjectNameparentQObject(QObject*)"  
    "QObject()"  
    };  

    这个字符串都是些什么内容呀?有,Class Name, Signal Name,Slot Name, Property Name。看到这些大家是不是觉得很熟悉呀,对啦,他们就是MetaSystem所支持的最核心的功能属性了。

    const unit *data,这个指针指向一个正整数数组,只不过在不同的object中数据的长度不一定相同,这取决于与之相应的class中定义了多少signal、slot和property。

    这个整数数组的值,有一部分指出了前一个变量(stringdata)中不同字符串的索引值,但是需要注意的是,这里面的数值并不是直接标明了每一个字符串的索引值,这个数组还需要通过一个相应的算法计算后,才能获得正确的字符串的索引值。

    下面是QObject::staticMetaObject.d.data指针指向的正整数数组的值如下:

    static const uint qt_meta_data_QObject[] = 
    {
           //content:
           2,       //revision
           0,       //classname
           0,       0,       //classinfo
           4,       12,       //methods
           1,       32,       //properties
           0,       0,       //enums/sets
           2,       35,       //constructors
           //signals:signature,parameters,type,tag,flags
           9,       8,       8,       8,       0x05,
           29,     8,       ,8       8,       0x25,
           //slots:signature,parameters,type,tag,flags
           41,       8,       8,       8,       0x0a,
           55,       8,       8,       8,       0x08,
           //properties:name,type,flags
           90,       82,       0x0a095103,
           //constructors:signature,parameters,type,tag,flags
           108,       101,       8,       8,       0x0e,
           126,        8,          8,       8,       0x2e,
           0       //end
    };

    第一个section,就是 //content 区域的整数值,这一块区域在每一个QMetaObject的实体对象中数量都是相同的,含义也相同,但具体的值就不同了。专门有一个struct定义了这个section,其含义在上面的注释中已经说的很清楚了。

    struct QMetaObjectPrivate  
    {  
          int revision;  
          int className;  
          int classInfoCount, classInfoData;  
          int methodCount, methodData;  
          int propertyCount, propertyData;  
          int enumeratorCount, enumeratorData;  
          int constructorCount, constructorData;  
    };  

    第二个section,以 // signals 开头的这段。这个section中的数值指明了QObject这个class包含了两个signal,
    第三个section,以 // slots 开头的这段。这个section中的数值指明了QObject这个class包含了两个slot。
    第四个section,以 // properties 开头的这段。这个section中的数值指明了QObject这个class包含有一个属性定义。
    第五个section,以 // constructors 开头的这段,指明了QObject这个class有两个constructor。

    const void *extradata,这是一个指向QMetaObjectExtraData数据结构的指针。

  • 相关阅读:
    ZooKeeper-3.3.4集群安装配置
    zookeeper原理(转)
    flume 转
    Flume NG 简介及配置实战
    Flume NG 配置详解
    '增量赋值(augmented assignment)', 多么痛的领悟!
    用matplotlib制作的比较满意的蜡烛图
    Spyder code editor里的小秘密: 右侧高亮提示
    pywinauto: 导入时遇到 "TypeError: LoadLibrary() argument 1 must be string, not unicode"
    爬取新浪财经个股的历史财报摘要
  • 原文地址:https://www.cnblogs.com/tgycoder/p/5524735.html
Copyright © 2011-2022 走看看