导读(反射简介)
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。即可以在运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods 即有类似如下语句
ClassA objA = ReflexCreat("ClassA")
这也是程序序列化的关键,可以吧相关的类对象以字符串的形式存贮到磁盘,然后利用反射机制读取序列化文件还原系统状态
然而遗憾的是 C++ 并不支持这一机制,但是在实际使用中这是一个很nice的功能,对与 C++ 的fans来说这个可是一个不怎么好的消息
但是考虑到MFC中的 序列化 功能,C++ 肯定也是有办法实现反射的功能的,下面就一起来探讨下C++ 反射机制的实现
一、实现(简单工厂)
学过设计模式的人对于 简单工程模式 肯定不会陌生,我们可以通过建立一个简单工厂模式,利用工厂通过类型名称帮助我们得到类的实体对象
代码形式如下
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Class CFactory { public: Object* FactoryCreater(const std::string& class_name) { if (class_name == "ClassA") return new ClassA; else if (class_name == "ClassB") return new ClassB; ... else return NULL; } }; |
客户端Client直接如下使用就好了
1 2 3 4 5 6 7 |
int main() { CFactory factory; ClassA objA = factory.FactoryCreater("ClassA"); ... return 0; } |
这样好像我们实现了反射这一简单功能,但是仔细想想这么做仿佛有所不妥,每添加一个新的类,我们就需要在工厂方法中添加分支语句,这样做不但使得代码复杂化,同时也违背了设计模式的开闭原则,所以我们需要换一种方法来解决这个问题
二、实现&进阶(自动注册hash表,回调函数)
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 41 42 |
//Reflex.h class ClassInfo; class Reflex { public: Reflex() {} virtual ~Reflex() {} //hash表注册 static bool Register(ClassInfo* pCInfo); static Reflex* CreatObject(std::string className); }; //Reflex.cpp static std::map<std::string, ClassInfo*> *m_classInfoMap = NULL; bool Reflex::Register(ClassInfo* pCInfo) { if (!m_classInfoMap) { m_classInfoMap = new std::map<std::string, ClassInfo*>(); } if (!pCInfo) { return false; } if (m_classInfoMap->end() == m_classInfoMap->find(pCInfo->m_className)) { m_classInfoMap->insert(std::map<std::string, ClassInfo*>::value_type(pCInfo->m_className, pCInfo)); } return true; } Reflex* Reflex::CreatObject(std::string className) { std::map<std::string, ClassInfo*>::const_iterator c_iter = m_classInfoMap->find(className); if (m_classInfoMap->end() != c_iter) { //当传入字符串name后,通过name找到info,然后调用对应的CreatObject()即可 return c_iter->second->CreateObject(); } return NULL; } |
- 定义回调函数函数指针,完成hash表自主注册的ClassInfo类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//Reflex.h //函数指针、指向创建类实例的回调函数 typedef Reflex* (*ObjConstructorFun)(); class ClassInfo { public: ClassInfo(const std::string className, ObjConstructorFun classConstructor) :m_className(className), m_objectConstructor(classConstructor) { //classInfo的构造函数是传入类名和类对应的new函数然后自动注册进map中 Reflex::Register(this); } virtual ~ClassInfo() {} Reflex* CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : NULL; } bool IsDynamic()const { return NULL != m_objectConstructor; } const std::string GetClassName()const { return m_className; } ObjConstructorFun GetConstructor()const { return m_objectConstructor; } public: std::string m_className; ObjConstructorFun m_objectConstructor; }; |
好了,到目前位置我们的 局 已经铺的差不多了,现在就是具体怎么用上我们的 反射模块
需要反射支持的CTest类的申明如下
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 |
//Test.h #include "Reflex.h" class CTest : public Reflex { public: CTest(); virtual ~CTest(); virtual ClassInfo* GetClassInfo() const { return &m_classInfo; } static Reflex* CreatObject() { return new CTest; } protected: static ClassInfo m_classInfo; }; //Test.cpp ClassInfo CTest::m_classInfo("CTest", CTest::CreatObject); CTest::CTest() { std::cout <<"ADDR: ["<< std::hex << (long)this << "] ,The Object Name is \"CTest\" construced!" << std::endl; } CTest::~CTest() { std::cout <<"ADDR: ["<< std::hex << (long)this << "] ,The Object Name is \"CTest\" destroyed!" << std::endl; } |
- 客户端Client调用情况如下
1 2 3 4 5 6 7 8 9 10 |
int main() { CTest* test = (CTest*)(Reflex::CreatObject("CTest")); delete test; return 0; } //输出情况: ADDR: [7b4d40] ,The Object Name is "CTest" construced! ADDR: [7b4d40] ,The Object Name is "CTest" destroyed! |
三、代码简化&宏(降低使用成本)
通过之前的代码我们已经在C++中实现了反射,但是可能大家发现了,每一个需要反射支持的类,我们都需要额外的添加一些代码,来保证反射模块的正常运作,而且这一部分除了类的名称之外就没有了什么不同。
聪明的你可能已经想到了,没错就是C+ +的宏,虽然C++的作者极力反对宏的使用,但是那啥你懂的(懒人就是理由多~),当然之前学习Windwos程序设计的时候,在MFC中微软的小哥哥们那才叫把宏玩的了个溜
- 言归正传,在基类Reflex.h中加入这一大段宏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//新申明类成员函数以及变量的注册 #define DECLARE_CLASS(class_name) \ public:\ virtual ClassInfo* GetClassInfo() const { return &m_classInfo; }\ static Reflex* CreatObject()\ {\ return new class_name;\ }\ protected:\ static ClassInfo m_classInfo; //新申明类ClassInfo注册 #define REGISTER_CLASS(class_name)\ ClassInfo class_name::m_classInfo(#class_name, class_name::CreatObject); //利用自写反射生成类对象 #define REFLEX_CLASS(class_name)\ (class_name*)(Reflex::CreatObject(#class_name)) |
- 于是乎,新申请的类就可以简化成酱紫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//Test.h class CTest : public Reflex { public: CTest(); virtual ~CTest(); DECLARE_CLASS(CTest) } //Test.cpp REGISTER_CLASS(CTest) CTest::CTest() { } CTest::~CTest() { } |
四、结语
在这儿说一下宏里面的东西吧
- 最简单的 \就是当前宏的定义除了本行下面还有,告诉编译器后面的一起处理
#define OUT(name) printf(#name);
- 以这个为例其中的 #name之前的 #就表示把name格式化为字符串
#define T(name) Class## name
- 这里的 ## name 中的 ##就是连接符的意思,Class和可变字符name连接成一个字符串
到此C++中简单反射的实现就告一段落,若有错误,欢迎指正
2017年4月15日 下午1:13 沙发
你的评论输入留言用户信息这为什么和我不一样呢?是你自己改的吧!
2017年4月15日 下午1:43 1层
@堆爱博客 是的基本都是自己改的