Qt的元对象系统的功能建立基础

  1. QObject类是所有使用元对象系统的类的基类
  2. 必须在一个类的开头部分插入宏Q_OBJECT,这样这个类才可以使用元对象系统的特性
  3. MOC为每个QObject的子类提供必要的代码来实现元对象系统的特性

构建项目时,MOC会读取C++源文件,当他发现类的定义里有Q_Object宏时,他就会为这个类生成另一个包含元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被标准C++编译器编译和连接。

QObject类

简述

QObject类是所有使用元对象系统的类的基类,也就是说,如果一个类的父类或上层父类时QObject,它就可以使用信号与槽、属性等特性。

QObject类与元对象系统特性相关的函数

元对象

QMetaObject *metaObject() 返回这个类的元对象
QMetaObject staticMetaObject 这是类的静态变量,不是函数,存储了类的元对象

类型信息

bool inherits() 判断这个对象是不是某个类的子类的实例

动态翻译

QString tr() 类的静态函数,返回一个字符串的翻译版本

对象树

QObjectList &children() 返回子对象列表
QObject *parent() 返回父对象指针
void setParent() 设置父对象
T findChild() 按照对象名称,查找可被转化为类型T的子对象
QList findChildren() 返回符合名称和类型条件的子对象列表

信号槽

QMetaObject::Connection connect() 设置信号与槽关联
bool disconnect() 解除信号与槽的关联
bool blockSignals() 设置是否阻止对象发射任何信号
bool signalsBlocked() 若返回true,表示对象被阻止发射信号

属性系统

QList dynamicPropertyNames() 返回所有动态属性名称
bool setProperty() 设置属性值,或添加动态属性
QVariant property() 返回属性值

元对象特性的实现

元对象(meta object)

每个QObject及其子类的实力都有一个元对象,这个元对象是自动创建的。静态变量staticQObject就是这个元对象,函数metaObject返回这个元对象指针,所以获取一个对象的元对象有两种方式,示意代码如下:
QPushButton *btn = new QPushButton();
const QMetaObject *metaPtr = btn->metaObject(); //获取元对象指针
const QMetaObject metaobj = btn->staticMetaObject; //获取元对象

类型信息

QObject的inherits()函数可以判断对象是不是从某个类继承的类的实例

动态翻译

函数tr() 用于返回一个字符串的翻译版本,在设计多语言界面的应用需要用到tr()函数

对象树(object tree)

对象树(object tree) 指的是表示对象间从属关系的树状结构。例如在一个窗口上,组件都有父容器,窗口时界面上所有组件的顶层容器。
Object类的parent()函数返回其父对象,children()函数返回其子对象,findChildren()函数可以返回某些子对象或所有子对象。
窗口和窗口上的所有组件就构成了对象树,窗口可以访问任意一个界面组件。
对象树中的某个对象被删除时,它的子对象会被自动删除,所以,一个窗口被删除时,它上面的所有界面组件也会被自动删除。

信号与槽

通过在一个类的定义中插入宏Q_OBEJECT,我们就可以使用Qt扩展的C++语言特性编程,例如在一个类中定义属性、类属性、信号和槽函数

属性系统

在类的定义代码中可以使用宏Q_PROPERTY定义属性,QObject的setProperty()函数会设置属性的值或定义动态属性;propertry()函数会返回属性的值

QMetaObject类

每个QObject及其子类的实例都有一个自动创建的元对象,元对象是MetaObject类型的实例。
元对象存储了类的实例所属类的各种元数据,包括信息元数据、方法元数据、属性元数据等,所以元对象实质上是对类的描述

QMetaObject类的主要接口函数

类的信息

char className() 返回这个类的类名称
QMetaType metaType() 返回这个元对象的元类型
MetaObject superClass() 返回这个类的上层父类的元对象
bool inherits(QMetaObject metaObject) 返回true表示这个类继承自metaObject描述得类,否则返回false
QObject newInstance(
) 创建这个类的一个实例,可以给构造函数传递最多10个参数

类信息元数据

QMetaClassInfo classInfo(int index) 返回序号为index的一条类信息的元数据,类信息是在类中用宏Q_CLASSINFO定义的一条信息
int indexOfClassInfo(char* name) 返回名称为name的类信息的序号,序号可用于classInfo函数
int classInfoCount() 返回这个类的类信息条数
int classInfoOffset() 返回这个类的第一条类信息的序号

构造函数元数据

int constructorCount() 返回这个类的构造函数的个数
QMetaMethod constructor(int index) 返回这个类的序号为index的构造函数的元数据
int indexOfConstructor(char *constructor) 返回一个构造函数的序号,constructor包括正则化之后的函数名和参数名

方法元数据

QMetaMethod method(int index) 返回序号为index的方法的元数据
int methodCount() 返回这个类的方法的个数,包括基类中定义的方法,方法包括一般的成员函数,还包括信号和槽
int methodOffset() 返回这个类的第一个方法的序号
int indexOfMethod(char* method) 返回名称为method的方法的序号

枚举类型元数据

QMetaEnum enumerator(int index) 返回序号为index的枚举类型的元数据
int enumeratorCount() 返回这个类的枚举类型个数
int enumeratorOffset() 返回这个类的第一个枚举类型的序号
int indexOfEnumerator(char *name) 返回名称为name的枚举类型的序号 *

属性元数据

QMetaProperty property(int index) 返回序号为index的属性的元数据
int propertyCount() 返回这个类的属性的个数
int propertyOffset() 返回这个类的第一个属性的序号
int indexOfProperty() 返回名称为name的属性的序号

信号与槽

int indexOfSignal(char *signal) 返回名称为singnal的信号的序号
int indexOfSlot(char *slot) 返回名称为slot的槽函数的序号

静态函数

bool checkConnectArgs(****) 检查信号与槽函数的参数是否兼容
void connectSlotsByName(QObject object) 迭代搜索object的所有子对象,将匹配的信号与槽连接起来
bool invokeMethod(
***) 运行QObject对象的某个方法,包括信号、槽或成员函数
QByteArray normalizedSignature(char *method)将方法method的名字和参数字符串正则化,去除多余空格。函数返回结果可用于checkConnetArgs(), indexOfConstructor()等函数

总结

通过QMetaObject类的这些函数,我们可以在运行时获取一个QObject对象的类信息和各种元数据。例如,函数className()可以返回类的名称,函数superClass()可以返回其父类的元对象,函数newInstance()可以创建元对象所描述类的一个新的实例。
类的元数据又可分为多种类型,且有专门的类来描述。例如,函数property返回属性的元数据,属性元数据用QMetaProperty类描述,它的接口函数描述了属性的各种特性,如函数name()返回属性名称,函数type()返回属性数据类型。

元对象系统的一些特性补充扩展

运行时类型信息

通过使用QObject和QMetaObject提供的以下一些接口函数,我们可以在运行时获得一个对象的类名称以及其父类的名称,判断其是否从某个类继承而来。要实现这些功能,我们并不需要C++编译器的运行时类型信息(run-time type information, RTTI) 支持。
(1) 函数QMetaObject::className()。这个函数可在运行时返回类名称的字符串。
(2) 函数QObject::inherits()。这个函数可以判断一个对象是不是继承自某个类的实例,顶层的父类是QObject。
(3) 函数QMetaObject::superClass()。这个函数返回该元对象所描类的父类的元对象,通过父类的元对象可以获取父类的一些元数据。
(4) 函数qobject_cast()。这个函数是头文件中定义的一个非成员函数,对于QObject及其子类的对象,可以使用函数qobject_cast()进行动态类型转换。如果自定义的类要支持函数qobject_cast(),那么自定义的类需要直接或间接继承自QObject,且在类定义中插入宏Q_OBJECT。
注: 标准C++语言中有类似的强制类型转换函数dynamic_cast(),使用qobject_cast()的好处是不需要C++编译器开启RTTI支持。

属性系统