Qt学习 -- Meta_Object Model系统

Meta_Object Model系统

Qt meta-object系统基于三个方面:

  1. QObject提供一个基类, 方便派生类使用meta-object系统的功能;
  2. Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号、槽;
  3. Meta Object编译器(MOC),为每个QObject派生类生成代码,已支持meta-object功能。

QObject定义了从一个QObject对象访问meta-object功能的接口Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能编译器在扫描一个源文件时如果发现类的声明中有这个宏就会生成一些代码来为支持meta-object功能——主要是生成该类对应 MetaObject类以及对QObject的函数override。

QObject和QMetaObject:
顾名思义QMetaObject包含了QObject的所谓的元数,也就是QObject信息的一些描述信息除了类型信息还包含QT中特有的signal&slot信息。

QObject::metaObject()方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的 metaobject。

如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal&slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QObject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。

除了为对象间的通信提供信号与槽(引入元对象系统的主要原因)机制外,元对象还提供以下特性:

  • QObject::metaObject()返回类关联的meta-object对象。
  • QMetaObject::className()在运行时以字符串的形式返回类名,无需C++编译器提供运行时类别信息(RTTI)的支持。
  • QObject::inherits()返回一个对象是否是QObject继承树上一个类的实例。
  • QObject::tr()和QObject::trUtf8()提供国际化支持,将字符串翻译成指定的语言。
  • QObject::setProperty()和QObject::property()通过名称动态设置和获取属性。
  • QMetaObject::newInstance()构造类的一个新实例。

类型转换

除此之外,还可以用qobject_cast()动态转换QObject类的类型。qobject_cast()函数和标准C++的dynamic_cast()功能类似,它的优点在于:不需要RTTI的支持,而且可以跨越动态连接库的转换。它尝试将它的参数转换成尖括号内的指针类型,如果对象是正确的类型(在运行时检查),则返回非零指针;否则,返回0,说明对象类型不兼容。

例如,假设MyWidget继承自QWidget,同时也声明了Q_OBJECT宏。

QObject *obj = new MyWidget;

QObject *类型的变量obj实际上指向一个MyWidget对象,因此,我们可以适当地进行类型转换:

QWidget *widget = qobject_cast<QWidget *>(obj);

因为obj实际上是一个MyWidget,而MyWidget是QWidget的子类,所以,从QObject转换为QWidget成功了。既然知道了obj是MyWidget类型的,那么我们也可以将其转换为MyWidget *:

MyWidget *myWidget = qobject_cast<MyWidget *>(obj);

到MyWidget类型的转换也是成功的,因为qobject_cast()并不区分内建的Qt类型和自定义类型。可是,转换到QLabel却失败了,返回的指针为0。

QLabel *label = qobject_cast<QLabel *>(obj);// label0

QMetaObject

每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。

QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:

1、基本信息

const char * className () constconst QMetaObject * superClass () const

2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。

int classInfoCount () const
int classInfoOffset () const
QMetaClassInfo classInfo ( int index ) const
int indexOfClassInfo ( const char * name ) const

3、contructor:提供该类的构造方法信息

QMetaMethod constructor ( int index ) const
int constructorCount () const
int indexOfConstructor ( const char * constructor ) const

4、enum:描述该类声明体中所包含的枚举类型信息

QMetaEnum enumerator ( int index ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const

5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。

QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const

int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int indexOfSlot ( const char * slot ) const

6、property:类型的属性信息

QMetaProperty property ( int index ) const
int propertyCount () const
int propertyOffset () const
int indexOfProperty ( const char * name ) const
QMetaProperty userProperty () const  //返回类中设置了USER flag的属性

注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。

moc文件分析

我们知道Qt不是使用的“标准的” C++语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit。所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。另外,我们还可以看出一点,moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了。

总结

至此,我们应该是对Qt Meta-Object Model有了一个清楚的认识

  • Qt提供了一个QObject的基类
  • Qt扩展了C++语法,提供了Q_OBJECT、Q_INVOKABLE、signals、slots、emit、SIGNAL,SLOT、Q_PROPERT、Q_ENUM、Q_FLAG、Q_CLASSINFO等宏,moc会识别这些宏并生成对应的moc源代码
  • Q_OBJECT宏中声明了QMetaObject的静态对象,QMetaObject中记录了classinfo、method、property、enum,并提供了metacall来操作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值