提供了一堆原来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

QMetaObject感觉跟Delphi的类之类有一拼,好好学一下的更多相关文章

  1. delphi 实体类 JSON 数组

    delphi 实体类 与JSON转换,序列化 TJson REST.JSON.pas   TJson.JsonToObjectTJson.ObjectToJsonString JsonEncode O ...

  2. 比较C++、Java、Delphi声明类对象时候的相关语法

    同学们在学习的时候经常会遇到一些问题,C++.Java.Delphi他们到底有什么不一样的呢?今天我们来比较C++.Java.Delphi声明类对象时候的相关语法.希望对大家有帮助! C++中创建对象 ...

  3. Delphi 遍历类中的属性

    http://blog.csdn.net/easyboot/article/details/8004954 Delphi 遍历类中的属性 标签: delphistringbuttonclassform ...

  4. 转:Delphi的类与继承(VB与delphi比较)

    既然已经做出了com程序用delphi来开发的决定,那当然就要对delphi进行一些深入的了解.有人说delphi是一个用控件堆砌起来的工具,和vb没什么两样:也有人说dephi实际上是面向过程的,他 ...

  5. 再次深入理解delphi的类

    property WindowState: TWindowState read FWindowState write SetWindowState; {声明一个属性WindowState,它从字段FW ...

  6. 获取Delphi所有类的类信息

    Delphi遍历进程中所有Class的TypeInfo,即便是在implementation中的class或者其他 class的private的子class. 一般普通EXE中的TypeInfo存放在 ...

  7. DELPHI学习---类和对象(五篇)

    Classes and objects(类和对象) 类(或者类类型)定义了一个结构,它包括字段(也称为域).方法和属性:类的实例叫做对象:类的字段.方法和属性被称为它的部件(components)或成 ...

  8. 理解 Delphi 的类(十一) - 深入类中的方法[8] - 抽象方法与抽象类

    //抽象方法类似与接口; 在没有接口的年代 Delphi 是用抽象方法来模拟接口的; 我想它最终会被接口替代. {下面就定义了两个抽象方法} TMyClass = class(TObject)   p ...

  9. delphi TComponent类 2

    来自:http://blog.csdn.net/lailai186/article/details/7442385 ------------------------------------------ ...

随机推荐

  1. oracle设定用户密码使用时间

    强制用户定期更换密码,要怎么设置? 假设密码用10天之后必须修改,宽限期为2天: 把电脑时间往后调十天,然后登录: 系统提示用户密码两天内失效,这时把电脑系统再往后调两天,然后登录: 系统提示密码已经 ...

  2. GUI编程笔记(java)04:GUI(HelloWorld)窗体案例

    1.Frame 在JAVA中,Frame是一种控件,可作为父窗体加载其他swing控件.案例: package cn.itcast_01; import java.awt.Frame; public ...

  3. dll注册到GAC还是bin - sharepoint程序

    通常来说程序在使用dll的时候,会先去GAC中查找是否有存在合适的dll,然后才会到应用程序下的bin目录去查找: 前几天遇到了一个奇葩问题,web项目工程添加了一个第三方dll的引用,然后把这个第三 ...

  4. CentOS7安装vim7.4

    卸载自带vim yum remove vim-enhanced vim-common 下载vim包 wget ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz ...

  5. HDU-1113(map的运用)

    Word Amalgamation Problem Description In millions of newspapers across the United States there is a ...

  6. github上建站和使用markdown写文章

    积累了那么久,终于成功搭建了github上的个人网站.虽然方法有点巧妙.不是还是建成了 同时学会用markdown写基本的文章.感觉还可以.附带我的github上的静态页面网站的网址:http://z ...

  7. 网站集A的子网站B上没有解决方案C发布的webpart

    在A的主站点,查看解决方案C在该网站集的feature,若未打开,则打开:若已经打开,可以先关闭再打开一次. 如果问题依然存在,可以查看子网站B上的feature是否打开,若未打开,则打开:若已经打开 ...

  8. MVC 中的Areas支持

    在ASP.NET MVC 2中对于Area功能的增强,这样的增强是如何在同一个项目中更好地组织应用程序的? ASP.NET MVC 1.0时,如果我们要在一个项目中做自己网站的后台应用,而又保持URL ...

  9. JQuery Datatables(一)

    最近项目中用了Bootstrap的样式风格,控件用了JQuery Datatables,主要有几下几点目标: 实现BootStrap风格的table,使用Ajax获取数据,并有勾选项 可以实现全选,单 ...

  10. Apache Hadoop2.0之HDFS均衡操作分析

    1 HDFS均衡操作原理 HDFS默认的块的副本存放策略是在发起请求的客户端存放一个副本,如果这个客户端在集群以外,那就选择一个不是太忙,存储不是太满的节点来存放,第二个副本放在与第一个副本相同的机架 ...