QMetaObject感觉跟Delphi的类之类有一拼,好好学一下
提供了一堆原来C++没有的功能,比如反射什么的。。。但是可能还是没有Delphi的类之类更强,因为类之类可以“创建类”。可惜我学艺不精,对“类之类”也没有完全学会。先留个爪,有空把两个东西都好好学学,再来个比较。。。
-----------------------------------------------------------------------------------
除了D指针,Qt中另一个很有意思的部分就是Q_OBJECT宏了。该宏提供了对元对象的访问,使得能够使用比如信号和槽等QObject的更多特性。元对象提供了诸如类名、属性和方法等的信息,也被称为“反射”。
通过使用QMetaObject,我们能够用如下代码显示一些类的信息:
- QObject obj;
- const QMetaObject *metaObj = obj.metaObject();
- qDebug() << "class name: " << metaObj->className();
- qDebug() << "class info count: " << metaObj->classInfoCount();
- qDebug() << "methods: ";
- // 从QMetaObject::methodOffset()开始打印,使其不会显示父类的方法
- for (int i = metaObj->methodOffset(); i < metaObj->methodCount(); ++i)
- qDebug() << metaObj->method(i).methodType() << " " << metaObj->method(i).signature();
由于C++并没有提供对这些信息的任何支持,Qt引入了元对象编译器(moc)来完成相应的工作。moc会读取每个头文件,如果发现其中定义的类是继承自QObject,且定义了Q_OBJECT宏,便会创建一个相应的C++源代码文件(moc_*.cpp),来完成这些工作。通过代码生成的工作,Qt不仅能够获得诸如Java等语言的灵活性,还能很好的保证继承自C++的性能和可扩展性。
假设我们有如下所示的简单类:
- class MyObject : public QObject
- {
- Q_OBJECT
- public:
- explicit MyObject(QObject *parent = 0);
- void myFunc();
- public slots:
- void mySlot(int myParam);
- signals:
- void mySignal(int myParam);
- };
moc会自动创建以下信息:
- // 保存在QMetaObject::d.data指向的空间,其起始部分是一个QMetaObjectPrivate结构体
- static const uint qt_meta_data_MyObject[] = {
- 5, // 版本号,其内部结构在Qt开发中有所改变
- 0, // 类名,其值为字符串qt_meta_stringdata_MyObject的偏移量
- // 以下值为(数量,索引)对
- 0, 0, // 类信息
- 2, 14, // 这里定义了两个方法,其起始索引为14(即signal部分)
- 0, 0, // 属性
- 0, 0, // 枚举
- 0, 0, // 构造函数
- 0, // 标识
- 1, // signal数量
- // 对于signal、slot和property,其signature和parameters为字符串qt_meta_stringdata_MyObject的偏移量
- // signals: signature, parameters, type, tag, flags
- 18, 10, 9, 9, 0x05,
- // slots: signature, parameters, type, tag, flags
- 32, 10, 9, 9, 0x0a,
- 0 // eod
- };
- // 保存在QMetaObject::d.stringdata指向的空间
- static const char qt_meta_stringdata_MyObject[] = {
- "MyObject/0/0myParam/0mySignal(int)/0"
- "mySlot(int)/0"
- };
以上信息,及其基类的相关信息,都保存在该类对应的元对象中:
- const QMetaObject MyObject::staticMetaObject = {
- { &QObject::staticMetaObject, // 指向其基类的元对象,保存在QMetaObject::d.superdata
- qt_meta_stringdata_MyObject, qt_meta_data_MyObject, 0 }
- };
这样,如果我们希望对QObject的对象进行类型转换,就不需使用开销较大的运算符dynamic_cast, 而能够直接使用qobject_cast。该模板函数利用了元对象系统的信息,避免了在运行时进行类型转换:
- template <class T> inline T qobject_cast(QObject *object)
- {
- #if !defined(QT_NO_QOBJECT_CHECK)
- reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object));
- #endif
- return static_cast(reinterpret_cast(0)->staticMetaObject.cast(object));
- }
这里,目标类型的元对象仅仅检查其是否从自身继承而来:
- const QObject *QMetaObject::cast(const QObject *obj) const
- {
- if (obj) {
- const QMetaObject *m = obj->metaObject();
- do {
- if (m == this)
- return obj;
- } while ((m = m->d.superdata));
- }
- return 0;
- }
此外,moc会为每一个信号创建相应函数。当信号被emit时,该函数会被自动调用:
- void MyObject::mySignal(int _t1)
- {
- void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
- // 检查链接到该信号的所有slot,并根据链接类型进行调用
- QMetaObject::activate(this, &staticMetaObject, 0, _a);
- }
最后,这些信号都会通过moc创建的qt_metacall函数被调用:
- int MyObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
- {
- // 如果该函数已被基类调用,则直接返回
- _id = QObject::qt_metacall(_c, _id, _a);
- if (_id < 0)
- return _id;
- // 根据函数的ID进行调用
- if (_c == QMetaObject::InvokeMetaMethod) {
- switch (_id) {
- case 0: mySignal((*reinterpret_cast< int(*)>(_a[1]))); break;
- case 1: mySlot((*reinterpret_cast< int(*)>(_a[1]))); break;
- default: ;
- }
- // 删除被该类“消耗”的ID,使得其子类类在处理时ID总是从0开始,而返回值-1则表示该函数已被调用
- _id -= 2;
- }
- return _id;
- }
参考:http://blog.csdn.net/seanyxie/article/details/6120040
QMetaObject感觉跟Delphi的类之类有一拼,好好学一下的更多相关文章
- delphi 实体类 JSON 数组
delphi 实体类 与JSON转换,序列化 TJson REST.JSON.pas TJson.JsonToObjectTJson.ObjectToJsonString JsonEncode O ...
- 比较C++、Java、Delphi声明类对象时候的相关语法
同学们在学习的时候经常会遇到一些问题,C++.Java.Delphi他们到底有什么不一样的呢?今天我们来比较C++.Java.Delphi声明类对象时候的相关语法.希望对大家有帮助! C++中创建对象 ...
- Delphi 遍历类中的属性
http://blog.csdn.net/easyboot/article/details/8004954 Delphi 遍历类中的属性 标签: delphistringbuttonclassform ...
- 转:Delphi的类与继承(VB与delphi比较)
既然已经做出了com程序用delphi来开发的决定,那当然就要对delphi进行一些深入的了解.有人说delphi是一个用控件堆砌起来的工具,和vb没什么两样:也有人说dephi实际上是面向过程的,他 ...
- 再次深入理解delphi的类
property WindowState: TWindowState read FWindowState write SetWindowState; {声明一个属性WindowState,它从字段FW ...
- 获取Delphi所有类的类信息
Delphi遍历进程中所有Class的TypeInfo,即便是在implementation中的class或者其他 class的private的子class. 一般普通EXE中的TypeInfo存放在 ...
- DELPHI学习---类和对象(五篇)
Classes and objects(类和对象) 类(或者类类型)定义了一个结构,它包括字段(也称为域).方法和属性:类的实例叫做对象:类的字段.方法和属性被称为它的部件(components)或成 ...
- 理解 Delphi 的类(十一) - 深入类中的方法[8] - 抽象方法与抽象类
//抽象方法类似与接口; 在没有接口的年代 Delphi 是用抽象方法来模拟接口的; 我想它最终会被接口替代. {下面就定义了两个抽象方法} TMyClass = class(TObject) p ...
- delphi TComponent类 2
来自:http://blog.csdn.net/lailai186/article/details/7442385 ------------------------------------------ ...
随机推荐
- 15个Linux Yum命令实例--安装/卸载/更新
在linux环境中, 安装, 卸载更新包是一个极为常见的操作.大部分的linux环境都提供了包的管理工具, 例如:apt-get, dpkg, rpm, yum等等. 一些Linux环境中,yum是默 ...
- codeforces 132C Logo Turtle(dp)
可以用三维dp来保存状态, dp[i][j][k]表示在前i个字符变换了j步之后方向为k(k = 1 or k = 0)的最优解,也就是离原点的最大距离.这里规定0方向为正方向,1位负方向,表示的是当 ...
- C#发送邮件-C#教程
如何利用C#实现邮件发送功能?闲话不多说请看代码: public static void SendMail(MyEmail email){//发送验证邮箱邮件.//1.创建邮件MailMessage ...
- 多线程(Thread),其实很简单!
目录: 1:线程简介 2:怎么操作线程 3:Thread的常用方法 4:简单的获奖机 5:应用程序域 线程:是Windows任务调度的最小单位.线程是程序中的一个执行流,每个线 ...
- MySQL存储过程详解 mysql 存储过程
原文地址:MySQL存储过程详解 mysql 存储过程作者:王者佳暮 mysql存储过程详解 1. 存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储 ...
- js获取元素transform参数得出的个人理解
之前写页面的时候有试过想用js获取某些元素的translate的数值什么的,但是translate又是transform的子样式(勉强说说),理所当然就是先获取transform样式,再读里面的值. ...
- IIS 上发布ASP.NET5
原文 http://docs.asp.net/en/latest/publishing/iis.html 1.安装HTTP Platform Handler ,这个是必需的,不然无法通过IIS访问AS ...
- ASP.NET项目中使用CKEditor +CKFinder 实现上传图片
CKEditor是什么 CKEidtor是一个在线富文本编辑器,可以将让用户所见即所得的获得编辑在线文本,编辑器或自动将用户编辑的文字格式转换成html代码. 在ASP.NET工程中添加CKEdito ...
- [Cookie] C#CookieHelper--C#操作Cookie的帮助类 (转载)
点击下载 CookieHelper.rar 下面是代码大家看一下 /// <summary> /// 类说明:CookieHelper /// 联系方式:361983679 /// 更新网 ...
- C#当中的多线程_任务并行库(下)
4.8 处理任务中的异常 下面这个例子讨论了任务当中抛出异常,以及任务异常的获取 class Program { static void Main(string[] a ...