Qt之美(二)元对象

来源:互联网 发布:会员卡刷卡软件 编辑:程序博客网 时间:2024/04/30 00:25

除了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_castint(*)>(_a[1]))); break;  
  11.     case 1: mySlot((*reinterpret_castint(*)>(_a[1]))); break;  
  12.     default: ;  
  13.     }  
  14.     // 删除被该类“消耗”的ID,使得其子类类在处理时ID总是从0开始,而返回值-1则表示该函数已被调用  
  15.     _id -= 2;  
  16.   }  
  17.   return _id;  
  18. }  
 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 pr 显示素材丢失怎么办 儿童上课小动作多怎么办 小孩锁骨骨裂怎么办 小孩子不爱写作业怎么办 小孩不交作业怎么办 小孩子不喜欢写作业怎么办 实行两票制 一品红会怎么办 跳舞之后浑身疼怎么办 事业编体检怀孕怎么办 辞职后报到证怎么办 学生早上起床困怎么办 延期毕业报到证怎么办 学校报到证丢失怎么办 生育险不满一年怎么办 产假结束上班宝宝怎么办 辞职后生育津贴怎么办 员工要不到工资怎么办 试用期不发工资怎么办 单位不给探亲假怎么办 工作5.5天周六怎么办 不爱运动的幼儿怎么办 总跟孩子生气怎么办 小孩经常感冒发烧怎么办 家里出现皮球虫怎么办 第一天教幼儿园怎么办 幼师遇到家长投诉怎么办 小孩喜欢玩手机怎么办 幼师掐孩子胳膊怎么办 孩子喜欢抓人怎么办 小孩顽皮不听教怎么办 出现了心理异常怎么办 想问下心理出问题该怎么办 大班幼儿爱说话怎么办 孩子不喜欢去幼儿园怎么办 别人抢宝宝玩具怎么办 新生儿抱淘气了怎么办 野兔子吃葵花怎么办 烫伤了怎么办安全反思 安全教案触电了怎么办 烫伤了怎么办儿歌导入 生病了怎么办教学反思