版权声明:本文为CSDN博主「YzlCoder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/y1196645376/article/details/51455273
前言
JAVA有着一个非常突出的动态相关机制Reflection
,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。然而C++不支持反射机制,虽然C++有RTTI(运行时类型识别)。但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。
ClassT* obj = FactoryCreate("ClassT");
类似于以上的语法,虽然C++没有自带的语法可以实现,但是我们可以自己通过其他方法来实现。
实现:
简单工厂模式
我们很容易可以想到可以使用简单工厂模式来实现这个效果:比如
// 这个是所有需要实现反射机制的类需要继承的基类,然后派生出来的类只需要再实现这个ToString即可。
class Object
{
public:
virtual string ToString() = 0;
};
class MyClass :public Object
{
public:
virtual string ToString(){
return "MyClass"; }
};
然后就是用于产生对象的工厂。
Object* FactoryCreat(const string& className)
{
if (className == "ClassA")
return new ClassA;
else if (className == "ClassB")
return new ClassB;
else if(className == "ClassC")
return new ClassC;
else if(className == "ClassD")
return new ClassD;
else if(className == "ClassE")
return new ClassE;
...
}
我们使用就可以这样:
int main()
{
Object* obj = FactoryCreat("MyClass");
cout << obj->ToString();
delete obj;
return 0;
}
我们使用简单工厂模式感觉好像是解决了问题,可以实现用字符串去new一个对应的对象,但是假如我们要新建一个类或者修改一个类,那么这个FactoryCreat都要进行修改。十分不利于维护。所以我们需要换一个方式来处理。
工厂模式+回调机制
首先我们要梳理一下这个方法的基本脉络:
- 工厂内部需要有个映射,也就是一个字符串对应一个类new的方法。
- 工厂给出一个接口,我们传入字符串,那么返回这个字符串对应的方法new出来的对象指针。
- 我们新建的类,如果需要支持反射机制,那么这个类需要自动将自己的new方法和名字注册到工厂的映射中。
OK,如果我们能完成以上几个要求,那么我们在类进行拓展的时候需要改动的地方就十分少了。对于工厂的代码我们基本上是不会改变的。也就基本上实现了我们C++反射机制的基本功能。
下面我们来一步一步解析代码:
首先我们还是需要一个Object作为需要支持反射机制类的基类
//Reflex.h
class Object
{
public:
Object() {
}
virtual ~Object() {
}
public:
static bool Register(ClassInfo* pClassInfo); // 注册传入一个classInfo(类信息),将这个类的信息注册到映射中
static Object* CreateObject(const std::string& className); // 工厂生产对象的接口
};
然后是实现:
//Reflex.cpp
std::map<string, ClassInfo*> gClassInfoMap;
bool Object::Register(ClassInfo* pClassInfo)
{
if (nullptr == pClassInfo) return false;
std::string className = pClassInfo->GetClassName();
if (gClassInfoMap.find(className) != gClassInfoMap.end()) return false;
gClassInfoMap.insert({
className, pClassInfo }); // 类名 <-> classInfo
return true;
}
Object* Object::CreateObject(const std::string& className)
{
auto iter = gClassInfoMap.find(className);
if (iter == gClassInfoMap.end())
return nullptr;
return iter->second->CreateObject(); //当传入字符串name后,通过name找到info,然后调用对应的CreatObject()即可
}
剩下的我们还需要一个classinfo类
就大功告成了:
//Reflex.h
typedef Object*(*ConstructorFunc)(void);
class ClassInfo
{
public:
ClassInfo(const std::string className, ConstructorFunc ctor)
:m_className(className), m_objectConstructor(ctor)
{
Object::Register(this); // classInfo的构造函数是传入类名和类对应的new函数然后自动注册进map中
}
~ClassInfo() {
}
public:
std::string GetClassName() {
return m_className; }
public:
Object* CreateObject() {
return (*m_objectConstructor)(); }
private:
string m_className;
ConstructorFunc m_objectConstructor;
};
有了这些类后,我们只需要让需要支持反射的类满足以下要求即可:
- 继承Object类。
- 重载一个CreatObject()函数,里面 return new 自身类。
- 拥有一个classInfo的成员并且用类名和CreatObject初始化。
满足以上三个要求的类我们就可以利用反射机制来创建对象了。我们可以看下面的例子:
class DerivedObject : public Object
{
public:
DerivedObject () {
}
~DerivedObject () {
}
public:
static Object* CreateObject() {
return new DerivedObject; }
protected:
static ClassInfo ms_classinfo;
};
ClassInfo DerivedObject ::ms_classinfo("DerivedObject", DerivedObject ::CreateObject);
使用的话我们就只需要调用Object::CreatObject(const std::string& className)
传入类名即可。
int main()
{
Object* obj = Object::CreateObject("DerivedObject");
delete obj;
return 0;
}
基本上反射机制的功能就实现了,而且使用回调注册在后期拓展上也容易维护。
使用宏简化代码:
其实大家发现,因为我们要让类支持反射那么就要满足我们上面的那三个要求,但是每个类都要写这样相似的东西。仔细一看,包括函数申da’s明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?
那就是使用宏。
//Reflex.h
#define DECLARE_CREATEOBJECT(CLASS_NAME) \
public:\
static Object* CreateObject() {
return new CLASS_NAME; }
#define DECLEAR_CLASSINFO \
protected:\
static ClassInfo ms_classinfo;
#define INIT_CLASS_INFO(CLASS_NAME) \
ClassInfo CLASS_NAME::ms_classinfo(#CLASS_NAME, CLASS_NAME::CreateObject);
有了宏替换后,我们定义一个新的类。例如我们上面的类B就可以这样写:
class DerivedObject : public Object
{
public:
DerivedObject() {
}
~DerivedObject() {
}
DECLARE_CREATEOBJECT(DerivedObject)
DECLEAR_CLASSINFO
};
INIT_CLASS_INFO(DerivedObject)
这样不管以后需要添加、修改什么功能都只需要修改宏就可以了而不需要每个类每个类去添加、修改方法。
ok到这里基本上,c++反射机制的实现就大功告成了!。