zoukankan      html  css  js  c++  java
  • QMetaObject感觉跟Delphi的类之类有一拼,好好学一下

    提供了一堆原来C++没有的功能,比如反射什么的。。。但是可能还是没有Delphi的类之类更强,因为类之类可以“创建类”。可惜我学艺不精,对“类之类”也没有完全学会。先留个爪,有空把两个东西都好好学学,再来个比较。。。

    -----------------------------------------------------------------------------------

    除了D指针,Qt中另一个很有意思的部分就是Q_OBJECT宏了。该宏提供了对元对象的访问,使得能够使用比如信号和槽等QObject的更多特性。元对象提供了诸如类名、属性和方法等的信息,也被称为“反射”。


    通过使用QMetaObject,我们能够用如下代码显示一些类的信息:

    1. QObject obj;  
    2. const QMetaObject *metaObj = obj.metaObject();  
    3. qDebug() << "class name: " << metaObj->className();  
    4. qDebug() << "class info count: " << metaObj->classInfoCount();  
    5. qDebug() << "methods: ";  
    6. // 从QMetaObject::methodOffset()开始打印,使其不会显示父类的方法  
    7. for (int i = metaObj->methodOffset(); i < metaObj->methodCount(); ++i)  
    8.   qDebug() << metaObj->method(i).methodType() << " " << metaObj->method(i).signature();  

     

    由于C++并没有提供对这些信息的任何支持,Qt引入了元对象编译器(moc)来完成相应的工作。moc会读取每个头文件,如果发现其中定义的类是继承自QObject,且定义了Q_OBJECT宏,便会创建一个相应的C++源代码文件(moc_*.cpp),来完成这些工作。通过代码生成的工作,Qt不仅能够获得诸如Java等语言的灵活性,还能很好的保证继承自C++的性能和可扩展性

    假设我们有如下所示的简单类:

    1. class MyObject : public QObject  
    2. {  
    3.   Q_OBJECT  
    4. public:  
    5.   explicit MyObject(QObject *parent = 0);  
    6.   void myFunc();  
    7. public slots:  
    8.   void mySlot(int myParam);  
    9. signals:  
    10.   void mySignal(int myParam);  
    11. };  

     

    moc会自动创建以下信息:

    1. // 保存在QMetaObject::d.data指向的空间,其起始部分是一个QMetaObjectPrivate结构体  
    2. static const uint qt_meta_data_MyObject[] = {  
    3.   5,       // 版本号,其内部结构在Qt开发中有所改变  
    4.   0,       // 类名,其值为字符串qt_meta_stringdata_MyObject的偏移量  
    5.   // 以下值为(数量,索引)对  
    6.   0,    0, // 类信息  
    7.   2,   14, // 这里定义了两个方法,其起始索引为14(即signal部分)  
    8.   0,    0, // 属性  
    9.   0,    0, // 枚举  
    10.   0,    0, // 构造函数  
    11.   0,       // 标识  
    12.   1,       // signal数量  
    13.   // 对于signal、slot和property,其signature和parameters为字符串qt_meta_stringdata_MyObject的偏移量  
    14.   // signals: signature, parameters, type, tag, flags  
    15.   18,   10,    9,    9, 0x05,  
    16.   // slots: signature, parameters, type, tag, flags  
    17.   32,   10,    9,    9, 0x0a,  
    18.   0        // eod  
    19. };  
    20. // 保存在QMetaObject::d.stringdata指向的空间  
    21. static const char qt_meta_stringdata_MyObject[] = {  
    22.   "MyObject/0/0myParam/0mySignal(int)/0"  
    23.   "mySlot(int)/0"  
    24. };  

     

    以上信息,及其基类的相关信息,都保存在该类对应的元对象中:

    1. const QMetaObject MyObject::staticMetaObject = {  
    2.   { &QObject::staticMetaObject, // 指向其基类的元对象,保存在QMetaObject::d.superdata  
    3.     qt_meta_stringdata_MyObject, qt_meta_data_MyObject, 0 }  
    4. };  

     

    这样,如果我们希望对QObject的对象进行类型转换,就不需使用开销较大的运算符dynamic_cast, 而能够直接使用qobject_cast。该模板函数利用了元对象系统的信息,避免了在运行时进行类型转换:

    1. template <class T> inline T qobject_cast(QObject *object)  
    2. {  
    3. #if !defined(QT_NO_QOBJECT_CHECK)  
    4.   reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object));  
    5. #endif  
    6.   return static_cast(reinterpret_cast(0)->staticMetaObject.cast(object));  
    7. }  

     

    这里,目标类型的元对象仅仅检查其是否从自身继承而来:

    1. const QObject *QMetaObject::cast(const QObject *obj) const  
    2. {  
    3.   if (obj) {  
    4.     const QMetaObject *m = obj->metaObject();  
    5.     do {  
    6.       if (m == this)  
    7.         return obj;  
    8.     } while ((m = m->d.superdata));  
    9.   }  
    10.   return 0;  
    11. }  

     

    此外,moc会为每一个信号创建相应函数。当信号被emit时,该函数会被自动调用:

    1. void MyObject::mySignal(int _t1)  
    2. {  
    3.   void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };  
    4.   // 检查链接到该信号的所有slot,并根据链接类型进行调用  
    5.   QMetaObject::activate(this, &staticMetaObject, 0, _a);  
    6. }  

     

    最后,这些信号都会通过moc创建的qt_metacall函数被调用:

    1. int MyObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
    2. {  
    3.   // 如果该函数已被基类调用,则直接返回  
    4.   _id = QObject::qt_metacall(_c, _id, _a);  
    5.   if (_id < 0)  
    6.     return _id;  
    7.   // 根据函数的ID进行调用  
    8.   if (_c == QMetaObject::InvokeMetaMethod) {  
    9.     switch (_id) {  
    10.     case 0: mySignal((*reinterpret_cast< int(*)>(_a[1]))); break;  
    11.     case 1: mySlot((*reinterpret_cast< int(*)>(_a[1]))); break;  
    12.     default: ;  
    13.     }  
    14.     // 删除被该类“消耗”的ID,使得其子类类在处理时ID总是从0开始,而返回值-1则表示该函数已被调用  
    15.     _id -= 2;  
    16.   }  
    17.   return _id;  
    18. }  

     

    参考:http://blog.csdn.net/seanyxie/article/details/6120040

  • 相关阅读:
    python加载csv数据
    Android项目依赖库管理方式简介
    Android PhotoView基本功能实现
    Android ListView的header footer设置visibility gone不起作用
    [干货][EMIT]千行代码实现代理式AOP+属性的自动装配
    Emit实现DataRow转化成强类型的T
    有关docker新版的icc、iptables的一个巨坑
    Mac神器Iterm2的Shell Integration的用法和注意事项
    生成ssl证书文件
    python3 module中__init__.py的需要注意的地方
  • 原文地址:https://www.cnblogs.com/findumars/p/4746342.html
Copyright © 2011-2022 走看看