阿里巴巴网站做销售方案/郑州seo优化哪家好
反射的概念:
指程序在运行时,访问、检测和修改它本身状态或行为的一种能力。wikipedia
简单的来说,就是一种自描述和自控制的能力。如果联想到镜子,就可以很好的理解,你能通过镜子看到自己,包括自己的动作,自己的外表。唯一不同的地方是,计算机语言的反射能力还包含对看到的自己采取措施。
面向对象模型中的一个概念,反映了描述事物的对象的一个抽象特性。也可以描述为自省(Reflection)。
反射的作用
在计算机编程语言中,反射机制可以用来:
- 获取类型的信息,包括属性、方法
- 动态调用方法
- 动态构造对象
- 从程序集中获得类型
反射的缺点
- 性能:反射可以理解成是一种解释操作,这个过程总是要慢于直接调用的。当然,性能问题的程度是可以控制的,如果程序在很少涉及的地方使用,性能将不会是一个问题。适用于性能不敏感的部分。
- 反射模糊了程序内部实际发生的事情,会比直接代码更加复杂。增加了理解代码的难度。
缺点不能掩饰其优点,针对不同的场景使用合理的技术才是最高境界。
反射的使用场景
- 序列化(Serialization)和数据绑定(Data Binding)
- 远程方法调用(RMI)
- 对象/关系数据映射(O/R mapping)eg. Hibernate,实现数据和对象的持久化。
关于c++的反射
我们知道,Java是原生支持反射机制的,通过Class类可以通过名称获得类对象,进一步操作。Python也支持反射机制,可以通过globals()获取对象map,也可以通过inspect模块,提供了自省的方法。但是C++呢?C++原生不支持反射机制,RTTI(运行时类型识别)也仅仅提供了类型的判断。
开闭原则是设计模式的原则之一,对修改是封闭,对扩展开放。一般来说,需要我们对类进行抽象,针对抽象的类进行编程。许多的设计模式中,为了能够满足这一点,我们常常使用一个配置文件,映射字符串与类型。然后通过反射机制获得字符串对应的对象,然后自动装配以达到易于扩展的目的。
实际上,C++并不是对反射支持的很好,要支持动态和静态反射,还需要慢慢去寻找,以下给出一些资料
C++11 reflection library
RTTR 库
Boost.Mirror 库
Mirror C++ reflection library
Reflection in Qt with QObject
本文讨论如何在C++中实现简单的反射。
场景
-
C++序列化,与反序列化。序列化就是将对象编程二进制的形式存储在磁盘上,或者通过网络传输给另一台机器。反序列化就是序列化的逆过程。但是这个逆过程,必须要根据字符串来判断将二进制流转化成什么类型的对象。
-
工厂模式,常常是根据一个字符串来获取想要的对象。但是为了满足开闭原则,我们不能简单的在工厂类中不断的修改生产函数来扩展不同的类型。这个时候,需要利用反射,使用抽象类。
设计模式之工厂模式:https://www.cnblogs.com/yssjun/p/11102162.html
实现
思路是:
- 使用map,映射字符串和生产函数
- 每次构造新类型时,将生产函数注册到map中
- 工厂函数通过map获得生产函数,建造不同的对象
QT类型反射
Qt的元对象系统除了提供信号/槽机制的特性之外,它还提供了以下特性:
QObject::metaObject() | 返回与该类绑定的meta-object元对象 |
QMetaObject::className() | 在运行时状态下以字符串的形式返回类名。不需要C++编译器原生的运行时类型信息(RTTI)的支持。 |
QObject::inherits() | 判断类的继承关系,返回继承信息:对象是否是QObject继承树上一个类的实例。 |
QObject::tr(),QObject::trUtf8() | 提供国际化支持,将字符串翻译成指定的语言 |
QObject::setProperty(),QObject::property() | 通过名称来动态设置和获取属性 |
QMetaObject::newInstance() | 创建新实例 |
通过QObject::metaObject()方法, 所有继承于QObject的类可以 返回元对象系统为其生成的metaObject对象。QMetaObject提供的一些重要信息:
QMetaClassInfo | 通过宏Q_CLASSINFO的支持,提供类的附加信息 |
QMetaEnum | Qt特色的枚举对象,支持key和 value之间的互转 |
QMetaMethod | 提供类成员函数的元数据 |
QMetaProperty | 提供类成员属性的元数据 |
Qt反射前期准备
1、首先得继承于Q_Object,同时需要在class中加入Q_OBJECT。
2、注册类成员变量需要使用Q_PROPERTY
Q_PROPERTY( type member READ get WRITE set) 其中READ,WRITE是关键字
Type表示成员的类型(不支持自定义类型,对Qt很多基本类型都支持);
Member代表你给该成员另外起的名字,可以和变量名不同;get,set就是自己在C++函数里面定义的基本的访问函数名,不需要写参数。
3、注册类成员函数
如果你希望这个函数能够被反射,那么很简单,只需要在类的函数声明前加入Q_INVOKABLE关键字。使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起。
举个例子
//声明一个继承于QObject的类 class ReflectionObject : public QObject {Q_OBJECTQ_PROPERTY(int Id READ Id WRITE setId)Q_PROPERTY(QString Name READ Name WRITE setName)Q_PROPERTY(QString Address READ Address WRITE setAddress)Q_PROPERTY(PriorityType Priority READ Priority WRITE setPriority)Q_ENUMS(PriorityType)public:enum PriorityType {High,Low,VeryHigh,VeryLow};Q_INVOKABLE int Id() {return m_Id; }Q_INVOKABLE QString Name() { return m_Name; }Q_INVOKABLE QString Address() { return m_Address; }Q_INVOKABLE PriorityType Priority() const {return m_Priority; }Q_INVOKABLE void setId(const int & id) {m_Id = id; }Q_INVOKABLE void setName(const QString & name) {m_Name = name; }Q_INVOKABLE void setAddress(const QString & address) {m_Address = address; }Q_INVOKABLE void setPriority(PriorityType priority) {m_Priority = priority; } private:int m_Id;QString m_Name;QString m_Address;PriorityType m_Priority; };
遍历该类的成员函数:
ReflectionObject theObject;const QMetaObject* theMetaObject =theObject.metaObject(); int methodIndex; int methodCount = theMetaObject->methodCount(); for(methodIndex = 0; methodIndex < methodCount; ++methodIndex) {QMetaMethod oneMethod =theMetaObject->method(methodIndex);qDebug() <<"typeName: " <<oneMethod.typeName();qDebug() <<"signature: " <<oneMethod.signature();qDebug() <<"methodType: " <<oneMethod.methodType();;qDebug() <<"parameterNames: " <<oneMethod.parameterNames() <<"\n"; }
其中typeName代表返回类型,signature只的是函数的原貌,methodType代表函数的类型,在Qt中分为三类(槽,信号,普通函数),parameterNames代表函数参数
遍历该类的成员变量:
int propertyIndex; int propertyCount = theMetaObject->propertyCount(); for(propertyIndex = 0; propertyIndex < propertyCount; ++propertyIndex) {QMetaProperty oneProperty =theMetaObject->property(propertyIndex);qDebug() <<"name: " << oneProperty.name();qDebug() <<"type: " <<oneProperty.type() <<"\n"; }
遍历该类的枚举集合:
int enumeratorIndex; int enumeratorCount = theMetaObject->enumeratorCount(); for(enumeratorIndex = 0; enumeratorIndex < enumeratorCount; ++enumeratorIndex) {QMetaEnum oneEnumerator =theMetaObject->enumerator(enumeratorIndex);int enumIndex;int enumCount = oneEnumerator.keyCount();for(enumIndex = 0;enumIndex < enumCount; ++enumIndex){qDebug() <<oneEnumerator.key(enumIndex) <<" ==> " <<oneEnumerator.value(enumIndex);} }
这里我们看到QMetaEnum存在key、value配对出现,这必然可以互转,而QMetaEnum确实提供了方式:valueToKey()、keyToValue()。
反向构建
在Qt5_C++中是使用newInstance()直接反向构建新对象。而在java语言中,可以使用getObject(String)函数,从类型直接构建新的对象。
代码如下:
//MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE explicit MyObject(QObject* parent = nullptr);
signals:
public slots:
private:
};
#endif // MYOBJECT_H
//MyObject.cpp
#include "MyObject.h"
MyObject :: MyObject(QObject* parent) : QObject(parent)
{
}
调用方法:
首先要注册你的class:(必须继承QObject)
QMap<QString, const QMetaObject*> classes;
classes["MyObject"] = &MyObject :: staticMetaObject;
反射构建一个新对象:
MyObject* my_object = qobject_cast<MyObject*>(classes["MyObject"]->newInstance());
MyObject* test001 = new MyObject(this);
MyObject* test002 = qobject_cast<MyObject*>(test001->metaObject()->newInstance());
反射的应用
反射反射,就我目前的认知水平来看,通过使用字符串,来实现函数的通用化调用,例如你可以利用反射把很多函数放置到数组中,实现一次遍历,全部调用。
目前见到的大多是利用反射来操作数据库,例如hibernate,其实可以利用Qt的反射,快速实现所谓的hibernate。