# 导读(反射简介)
Reflection
是 Java
程序开发语言的特征之一,它允许运行中的 Java
程序对自身进行检查,或者说 "自审",并能直接操作程序的内部属性。即可以在运行时加载、探知、使用编译期间完全未知的 classes
。换句话说, Java
程序可以加载一个运行时才得知名称的 class
,获悉其完整构造(但不包括 methods
定义),并生成其对象实体、或对其 fields
设值、或唤起其 methods
即有类似如下语句
ClassA objA = ReflexCreat("ClassA")
这也是程序序列化的关键,可以吧相关的类对象以字符串的形式存贮到磁盘,然后利用反射机制读取序列化文件还原系统状态
然而遗憾的是 C++
并不支持这一机制,但是在实际使用中这是一个很 nice 的功能,对与 C++
的 fans
来说这个可是一个不怎么好的消息
但是考虑到 MFC
中的 序列化 功能, C++
肯定也是有办法实现反射的功能的,下面就一起来探讨下 C++
反射机制的实现
# 一、实现(简单工厂)
学过设计模式的人对于 简单工程模式 肯定不会陌生,我们可以通过建立一个简单工厂模式,利用工厂通过类型名称帮助我们得到类的实体对象
代码形式如下
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 直接如下使用就好了
int main() | |
{ | |
CFactory factory; | |
ClassA objA = factory.FactoryCreater("ClassA"); | |
... | |
return 0; | |
} |
这样好像我们实现了反射这一简单功能,但是仔细想想这么做仿佛有所不妥,每添加一个新的类,我们就需要在工厂方法中添加分支语句,这样做不但使得代码复杂化,同时也违背了设计模式的开闭原则,所以我们需要换一种方法来解决这个问题
# 二、实现 & 进阶(自动注册 hash 表,回调函数)
需要解决的几个要点
定义回调函数指针,指向创建类实例的回调函数
一个带有 hash 表的工厂类,用 ' 类的名称 ' 和 ' 指向创建类实例的回调函数的函数指针 ' 作为键值对
实现 hash 表的自动注册
如果实现了以上几点,基本上 C++ 下简单的反射功能就完成了
现在我们来一步一步解决以上问题(将整个反射的实现封装到 Reflex.h 和 Reflex.cpp 中)
- 带有 hash 表的 Reflex 类的实现 (也是需要反射支持的类的基类)
//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 类
//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; | |
}; |
好了,到目前位置我们的 局 已经铺的差不多了,现在就是具体怎么用上我们的 反射模块
方法如下:
新定义的类若需要 反射支持 需要继承 基类 Reflex
在新类中定义并实现 返回自身对象指针的函数,用来作为回调函数指针指向的函数
持有一个静态 ClassInfo 对象,并使用类名和函数指针初始化,完成 hash 表的注册
需要反射支持的 CTest 类的申明如下
//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 调用情况如下
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 中加入这一大段宏
// 新申明类成员函数以及变量的注册 | |
#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)) |
- 于是乎,新申请的类就可以简化成酱紫
//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++ 中简单反射的实现就告一段落,若有错误,欢迎指正