Qt6 C++开发指南读书笔记-元对象系统概述
Qt的元对象系统的功能建立基础
- QObject类是所有使用元对象系统的类的基类
- 必须在一个类的开头部分插入宏Q_OBJECT,这样这个类才可以使用元对象系统的特性
- 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
信号槽
QMetaObject::Connection connect() 设置信号与槽关联
bool disconnect() 解除信号与槽的关联
bool blockSignals() 设置是否阻止对象发射任何信号
bool signalsBlocked() 若返回true,表示对象被阻止发射信号
属性系统
QList
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()。这个函数是头文件
注: 标准C++语言中有类似的强制类型转换函数dynamic_cast(),使用qobject_cast()的好处是不需要C++编译器开启RTTI支持。


