一、元对象概念
元对象功能:信号与槽、运行时类型信息、动态属性
二、元对象实现条件
1.该类必须继承自QObject类
2.类的私有区域必须声明Q_OBJECT宏,该宏用于启动元对象特性,使用动态特性,信号和槽
3.元对象编译器(moc)为每个QObject子类,提供了实现元对象特性必须的代码
三、元对象系统运行原则
1.moc编译器首先会去掉扩展语法当Qt程序启动了元对象属性时。
2.moc编译器是一个类似于qmake的工具,用于读取分析C++文件,若发现类包含Q_OBJECT宏的声明,会生成另外一分包含Q_OBJECT宏实现的C++源文件,被编译器一起编译。
3.元对象代码指的是通过moc编译器生成的源代码,其中包含Q_OBJECT宏的实现代码,moc工具的路径再E:\Qt5.14.1\5.14.1\mingw73_64\bin;
4.Q_OBJECT必须声明再头文件中,且头文件最好使用#ifndef #endif包含,防止重复引用
5…pro文件中必须有头文件和源文件
四、元对象系统与反射
1.反射模式:运行时,可以获取任意一个类对象的类型信息、属性、成员函数的机制
2.元对象系统提供功能之一为QObject派生类的对象提供运行时的类型信息、数据成员信息,即在运行时程序可以获取QObject派生类对象的类名称、父类名称、,该类成员函数、枚举类型等;
3.元对象:是指用于描述一个对象结构的的对象QMetaObject,
五、反射机制实现方法
1.QMetaObject类描述了QObject及其派生类的所有元信息,该类时元信息的核心类,通过该类的成员函数可以获取QObject类及派生类所有元信息;使用QMetaObject对象调用;
2.QMetaObject获取类成员函数方式,获取函数使用QMetaMethod、属性使用QMetaProperty进行描述;例如 QMetaMethod qm=metaObject->method(param);
六、Qt反射条件及原理
1.需要继承QObject类,声明宏Q_OBJECT;
2.注册成员函数,若希望普通成员函数能够被反射,需要在函数声明之前加入QObject::Q_INVOKABLE 宏。
3.注册成员变量:若希望成员变量能被反射,需要使用 Q_PROPERTY 宏。
4.Q_OBJECT 宏展开之后有一个虚拟成员函数 meteObject(),该函数会返回一个指向QMetaObject 类型的指针,通过该函数返回的指针调用 QMetaObject 类中的成员函数,便可查询到 QObject 及其派生类对象的各种信息。
5.moc识别 Qt 中特殊的关键字及宏,比如识别出 Q_PROPERTY 宏、Q_INVOKABLE宏、slot、signals 等。
代码示例:
class classname:public QObject
{
Q_OBJECT
public:
//定义 2 个构造函数、1 个信号、3 个函数。
Q_INVOKABLE QtGuiMeta(); //要想函数被反射,需要指定 Q_INVOKABLE 宏。
Q_INVOKABLE QtGuiMeta(int);
Q_INVOKABLE void f() {}
Q_INVOKABLE void g(int i, float j){}
void g1() {} //注意:此函数不会被反射。
public:
signals: void gb3();
};
class QtMetaChild :public classname
{
Q_OBJECT //要使用元对象系统,应在每个类之中都声明此宏
public:
//定义 1 个函数、2 个信号
Q_INVOKABLE void f() {}
signals:
void gb4();
void gb5();
};
testMeta()
{
QtGuiMeta ma;
QtGuiMetaChild mb; //创建两个对象
const QMetaObject* pa = ma.metaObject();
const QMetaObject* pb = mb.metaObject();
pa->methodCount();
pa->constructorCount();
//获取对象 ma 的成员函数 g 的元数据。
QMetaMethod m = pa->method(pa->indexOfMethod("g(int,float)"));
pb->methodOffset();
}
七、使用反射获取类对象信息
1.QMetaMethod类,用于描述对象成员函数,使用该类成员函数获取对象成员函数的信息
- enum MethodType{Method,signal,Slot,Constructor} 描述成员函数类型
- enum Access{Private,Public,Protected} 描述函数访问级别
- QByteArray methodSignature() const; 返回函数签名
- MethodType methodType() const; 返回函数类型
- QByteArray name()const; 返回函数名称
- int parameterCount() const; 返回函数参数个数
- QList parameterNames() const; 返回函数参数名称列表
- int parameterType(int index) const; 返回指定索引处的参数类型。返回值是使用 QMetaType 注册的类型,若类型未注册,则返回值为 QMetaType::UnknownType
- QList parameterTypes() const; 返回函数参数类型的列表
- int returnType() const; 返回函数的返回类型。返回值是使用 QMetaType 注册的类型,若类型未注册,则返回值为 QMetaType::UnknownType。
- const char * typeName() const; 返回函数的返回类型的名称
- Access access() const; 返回函数的访问级别(私有的、受保护的、公有的)
八、使用反射机制获取与类相关的信息
-
const char* className() const;
//获取类的名称,注意,若某个 QObject 的子类未启动元对象系统(即未使用 Q_OBJECT宏),则该函数将获取与该类最接近的启动了元对象系统的父类的名称,而不再返回该类的名称,因此建议所有的 QObject 子类都使用 Q_OBJECT 宏 -
const QMetaObject* superClass() const;
返回父类的元对象,若没有这样的对象则返回 0。代码示例:
class A:public QObject{ Q_OBJECT};
class B:public A{ Q_OBJECT};
class C:public QObject{Q_OBJECT};
class D:public C{};
void testMeta()
{
A ma; B mb; C mc; D md;
const QMetaObject *pa=ma.metaObject();
const QMetaObject *pb=mb.metaObject();
cout<<pa->className()<<endl; //输出类名 A
//使用 QMetaObject::inherits()函数判断继承关系。
cout<<pa->inherits(pa)<<endl; //输出 1,类被认为是自身的子类
cout<<pa->inherits(pb)<<endl; //输出 0,由 pb 所描述的类 B 不是类 A 的子类。
cout<<pb->inherits(pa)<<endl; //输出 1,由 pa 所描述的类 A 是类 B 的子类。
//使用 QObject::inherits()函数判断继承关系。
cout<<ma.inherits("B")<<endl; //输出 0,类 A 不是类 B 的子类。
cout<<ma.inherits("A")<<endl; //输出 1,类被认为是自身的子类
cout<<md.inherits("D")<<endl; //输出 0,因为类 D 未启动元对象系统。
cout<<md.inherits("C")<<endl; /*输出 1,虽然类 D 未启动元对象系统,但类 C 已启动,此种情形下
能正确判断继承关系。*/
cout<<md.metaObject()->className()<<endl; /*输出 C,此处未输出 D,因为类 D 未启动元对象系统,
与类 D 最接近的启动了元对象系统的父类是 C,因此返回 C。*/
}
4.qobject_cast
使用语法如下
- DestType* qobject_cast<DestType*>(QObject *p);
该函数类似于 C++中的dynamic_cast,但执行速度比 dynamic_cast 更快,且不需要C++的 RTTI 的支持,但
qobject_cast仅适用于 QObject 及其派生类。主要作用是把源类型 QObject 转换为尖括号中的目标类型DesType(或者子类型),并返回指向目标类型的指针,若转换失败,则返回 0。或者说源类型 QObject 属于目标类型DestType(或其子类型),则返回指向目标类型的指针,否则返回 0。 使用 qobject_cast 的条件:目标类型 DestType 必须继承(直接或间接)自 QObject,并使用 Q_OBJECT 宏。
代码示例
class A:public QObject{ Q_OBJECT
public:void fa(){cout<<"FA"<<endl;} };
class B:public A{ Q_OBJECT
public:void fb(){cout<<"FB"<<endl;} };
class C:public QObject{Q_OBJECT
public:void fc(){cout<<"FC"<<endl;} };
class D:public C{ public: void fd(){cout<<"FD"<<endl;} };
void g(QObject *p)
{
if(qobject_cast<A*>(p)) //若 p 是类 A 及其派生类类型
cout<<"GA"<<endl;
if(qobject_cast<B*>(p))//若 p 是类 B 及其派生类类型
cout<<"GB"<<endl;
else //若 p 不是类 B 及其派生类类型
cout<<"XX"<<endl;
}
void testMeta()
{
A *pa=new A; B *pb=new B; C *pc=new C; D *pd=new D;
qobject_cast<B*>(pa)->fb(); //输出 FB,转换成功后可调用子类中的函数。
//qobject_cast<D*>(pc); //错误,因为类 D 未使用 Q_OBJECT 宏。
g(pa); //输出 GA、XX。因为 pa 不是 B 及其派生类类型所以会输出 XX。
g(pb); //输出 GA、GB。因为 pb 是 A 的派生类类型,所以首先输出 GA,然后输出 GB。
g(pc); //输出 XX,因为 pc 即不是 A 也不是 B 及其派生类的类型,所以输出 XX。
g(pd); //输出 XX,原因同上。
}