Qt元对象反射机制

一、元对象概念

元对象功能:信号与槽、运行时类型信息、动态属性

二、元对象实现条件

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,原因同上。
	}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值