深入了解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信息包含:
- 信号表(signal table),与对应Qt类相关的系统定义及自定义的signal的名字。
- 槽表(slot table),与对应Qt类相关的系统定义及自定义的slot的名字。
- 类信息表(class info table),Qt类的类型信息。
- 属性表(property table),与对应类中的所有属性的名字。
- 指向parent meta object的指针。
请参考下图,Qt Meta Data Tables:

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

Q_OBJECT宏的定义如下:
staticMetaObject是静态常量,metaObject()是获取该对象指针的方法。所有QObject的对象都会共享staticMetaObject变量,靠它完成所有信号和槽的功能。
QMetaObject的定义如下:
- struct Q_CORE_EXPORT QMetaObject
- {
- // ......
- struct { // private data
- const QMetaObject *superdata;
- const char *stringdata;
- const uint *data;
- const void *extradata;
- } d;
- };
上面的代码就是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[] =
{
"QObject\0\0destroyed(QObject*)\0destroyed()\0"
"deleteLater()\0_q_reregisterTimers(void*)\0"
"QString\0objectName\0parent\0QObject(QObject*)\0"
"QObject()\0"
};
这个字符串都是些什么内容呀?有,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:
, //revision
, //classname
, , //classinfo
, , //methods
, , //properties
, , //enums/sets
, , //constructors
//signals:signature,parameters,type,tag,flags
, , , , 0x05,
, , , , 0x25,
//slots:signature,parameters,type,tag,flags
, , , , 0x0a,
, , , , 0x08,
//properties:name,type,flags
, , 0x0a095103,
//constructors:signature,parameters,type,tag,flags
, , , , 0x0e,
, , , , 0x2e,
//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数据结构的指针。
深入了解Qt(二)之元对象系统(Meta-Object System)的更多相关文章
- Qt对象模型之二:对象树与元对象系统
一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...
- Qt元对象系统简介
在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...
- qt 元对象系统
元对象系统 Qt中的元对象系统是用来处理对象间通讯的信号/槽机制.运行时的类型信息和 动态属性系统. 它基于下列三类: QObject类: 类声明中的私有段中的Q_OBJECT宏: 元对象编译器(mo ...
- QtCore是Qt的精髓(包括五大模块:元对象系统,属性系统,对象模型,对象树,信号槽)
作者:小豆君的干货铺链接:https://www.zhihu.com/question/27040542/answer/218384474来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
- Qt 元对象系统(Meta-Object System)
(转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...
- Qt笔记——元对象系统
Qt元对象系统提供了对象间的通信机制:信号和槽.以及执行类形信息和动态属性系统的支持.是标注C++的一个扩展,它使得Qt可以更好的实现GUI图形用户界面编程.Qt的元对象系统不支持C++模板.虽然模板 ...
- 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good
概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...
- 解析Qt元对象系统(四) 属性系统(确实比较方便)
官方解释 我们在Qt源码中可以看到一个QObject的子类经常会用到一些Q_开头的宏,例如QMainWindow类开始部分代码是这样的: Q_PROPERTY(QSize iconSize READ ...
- Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)
Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...
随机推荐
- HDU 1257 最少拦截系统(贪心)
解题思路:用一个vector存下数据,从头开始非递增遍历,并把符合条件的删除,一次操作,ans++,当vector为空时退出循环.(PS:学到了vector的erase操作,竟然还有返回值,涨姿势了) ...
- Red hat 5挂载U盘
装在虚拟机上的Linux 一.挂载U盘 ...
- sql server 2008 评估期已过期如何升级
1, http://wenku.baidu.com/view/49c88e2ab4daa58da0114af4.html 2, #BYTECH# ##
- JAVA算数运算符
算数运算符 序号 算数运算符 含义用法 特殊含义用法 1 + 加法 字符串连接 2 - 减法 3 * 乘法 4 / 除法 5 % 取余 实例: public class Test{ p ...
- AOP 下的权限控制实现
摘要 面向方面的编程(AOP)是一种新的编程技术,它弥补了面向对象的编程(OOP)在跨越模块行为上的不足.AOP 引进了 Aspect,它将影响多个类的行为封装到一个可重用模块中,它允许程序员对横切 ...
- setcookie各个参数详解
定义和用法 setcookie() 函数向客户端发送一个 HTTP cookie. cookie 是由服务器发送到浏览器的变量.cookie 通常是服务器嵌入到用户计算机中的小文本文件.每当计算机通过 ...
- SyntaxError: Non-UTF-8 code starting with '\xba' in file 错误的解决方法!!
第一次在Eclipse建立python工程,添加了自己新建的文件,写了一点代码,随后执行时候出现了错误,和昨天我在Visual Studio 2015里面一样,错误: SyntaxError: Non ...
- mac下Android开发环境搭建
之前一段时间在学习ios的开发,近一段时间想着也接触下Android开发,以来加深对移动端开发的理解.这里根据自己配置Android开发环境的过程,比较详细的来总结下自己的安装过程,希望对一些正准备配 ...
- shell+Jenkins+jmeter集成
参考http://www.cnblogs.com/ceshi2016/p/6025641.html 中除ant相关内容,shell替代ant循环执行jmeter脚本和将jtl转换为html job n ...
- Liferay 6开发学习(二十六):数据库连接相关问题
Liferay中怎么更换数据库? 常碰到有人问Liferay怎么更换数据库,怎么修改数据库连接.在我们第一次启动Liferay的时候,会有一个配置向导,在此配置向导我们可以选择数据库,并配置数据库连接 ...