一、反射
反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,那种程序能够“观察”并且修改自己的行为。它包括动态获取类型信息(包括方法和属性),动态创建对象,动态获取对象的方法,动态操作对象的属性。为了实现反射,必须给每个类添加元数据。这里我们只实现动态创建对象。
二、动态对象的创建
首先,我们首先想到是通过new "Circle"这样的方式创建,但是这在C++里面明显是行不通的。我们换一种方式,增加一个间接层,用一个工厂类负责对象的创建。我们传入一个字符串,工厂类返回这个字符串对应的对象。
Circle *p = DynObjectFactory::CreateObject("Circle")
为了让这个工厂能根据字符串创建对应对象,我们肯定需要在工厂里保存类字符串和对应创建对象的方法信息。这时我们可以定义一个map容器,把字符串作为key创建对象的方法作为value,因为我们并不知道要创建什么类型的对象,我们可以用void*指针作为创建方法的返回值。
typedef void* (*CREATE_FUNC)();
class DynObjectFactory
{
public:
static void* CreateObject(const string& name_)
{
map<string,CREATE_FUNC>::const_iterator it;
it = mapCls_.find(name_);
if (it != mapCls_.end())
return it->second(); //调用second函数
else
return 0;
}
private:
static map<string,CREATE_FUNC> mapCls_;
};
然后,我们希望我们自己的对象能注册到map中,我们可以给DynObjectFactory增加一个注册方法:
typedef void* (*CREATE_FUNC)();
class DynObjectFactory
{
public:
static void* CreateObject(const string& name_)
{
map<string,CREATE_FUNC>::const_iterator it;
it = mapCls_.find(name_);
if (it != mapCls_.end())
return it->second(); //调用second函数
else
return 0;
}
static void Register(const string& name_,CREATE_FUNC func_)
{
mapCls_.insert(make_pair(name_,func_));
}
private:
static map<string,CREATE_FUNC> mapCls_;
};
现在这个工厂类已经可以使用了,但是对于每一个类我们都需要调用Register手动注册到工厂,然后才能让工厂动态创建。这显然不够简洁。我们可以在定义一个Register类帮我们完成对象的注册:
class Register
{
public:
Register(const string& name_,CREATE_FUNC func_)
{
DynObjectFactory::Register(name_,func_);
}
};
为了能让我们自定义的类能自动调用Register类注册到工厂中,还需要定义一个宏:
#define REGISTER_CLASS(class_name)\
class class_name##Register \
{\
public:\
static void* NewIntance()\
{\
return new class_name;\
}\
private:\
static Register reg;\
};\
Register class_name##Register::reg(#class_name,class_name##Register::NewIntance);
在我们需要注册类的cpp文件里我们可以加上这个宏完成自动注册:
REGISTER_CLASS(Circle)
到这里,动态创建对象的功能已经完成了。但是它是如何工作的呢?
首先,在预处理期间,我们加入的宏REGISTER_CLASS(Circle)被展开为一个CircleRegister的类,这个类包含了Circle的创建方法和一个静态的注册方法对象。因为静态类型先于一切对象被初始化,所以在我们的对象创建之前,会初始化这个静态对象完成对象创建方法的注册。注册完成之后,我们就可以根据类字符串创建我们需要的对象:
Circle *p = DynObjectFactory::CreateObject("Circle")