发表于|更新于
|阅读量:
什么是反射?
反射是指在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。在 Java 中,反射是通过JVM实现的。JVM在得到class对象之后,再通过对class对象进行反编译,从而获取对象的各种信息。
可以参考 廖雪峰的官方网站 来帮助理解。Java 不是我们这篇文章的重点。
简单来说,如果编译不知道类或对象的具体信息,比如我们在搭 Java的SSH或者SSM框架的时候,类的名称放在XML文件中,属性和属性值放在XML文件中,需要在运行时读取XML文件,动态获取类的信息,此时就可以用反射来实现。
下面的代码来自 Java反射机制-十分钟搞懂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Test { public static void main(String[] args) throws Exception { Animal an = new Cat(); an.nickName ="旺财"; an.color = "黑色"; an.shout(); System.out.println(an); String className = "com.bjsxt.why.Cat"; Class clazz = Class.forName(className); Object an2 = clazz.newInstance(); } }
|
但是在C++ 中,C++ 只有非常弱的反射RTTI,但是C++ 的一些框架比如QT,UE自己实现了反射。这里参考 写给 C++ 程序员的反射教程。
Qt 中使用反射
reflect.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #pragma once
#include <QMetaObject> #include <QHash>
class Reflect { public: template<typename T> static void registerClass() { if(!constructors().contains(T::staticMetaObject.className())) { constructors().insert( T::staticMetaObject.className(), &constructorHelper<T> ); } }
static QObject* newInstance( const QByteArray& className, QObject* parent = nullptr ) { Constructor constructor = constructors().value( className ); if ( constructor == nullptr ) return nullptr; return (*constructor)( parent ); } private: typedef QObject* (*Constructor)( QObject* parent );
template<typename T> static QObject* constructorHelper( QObject* parent ) { return new T( parent ); }
static QHash<QByteArray, Constructor>& constructors() { static QHash<QByteArray, Constructor> instance; return instance; } };
|
想要实现反射的类要继承 QObject
并添加 Q_OBJECT
,为了能使类检测到成员变量和函数,还需要在变量和函数前面添加宏 Q_INVOKABLE
,这意味着该变量或函数在元对象系统编译该类时进行注册,在运行过程中能被元对象调用。例如:
dog.h
1 2 3 4 5 6 7 8 9 10 11 12
| #pragma once
#include <QObject>
class Dog : public QObject { Q_OBJECT public: Q_INVOKABLE explicit Dog(QObject* parent = nullptr);
Q_INVOKABLE void func(); };
|
dog.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "dog.h" #include <QDebug>
Dog::Dog(QObject* parent) : QObject(parent) { qDebug() << "instance created"; }
void Dog::func() { qDebug() << "fun loaded"; }
|
使用的时候如下:
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "dog.h" #include "reflect.h"
#include <QApplication>
int main(int argc, char *argv[]) { QApplication a(argc, argv);
Reflect::registerClass<Dog>();
QObject* obj = Reflect::newInstance("Dog"); QMetaObject::invokeMethod(obj, "func");
return a.exec(); }
|
可以看出,Qt的这种反射并不是真正意义上的反射,使用之前需要先进行注册。虽然可以在一个启动函数中注册所有的类,但还是比较麻烦的。
但是,通过将反射与Qt的property相结合,我们能够将类实现成类似于 Java 中的 bean 的东西,通过 get 和 set 方法取值和赋值。