这是一个很老的话题了。所谓的反射机制,其实就是MFC里面的对象动态创建。比如说,给一个字符串代表类名,比如”CMesh”,根据这个字符串创建出一个CMesh对象。有不同的实现方法,在MFC里面是用一张巨大的链表网络把所有的类的信息链接起来,里面存储了类名和构造函数,动态创建的时候按照一定的顺序去里面搜索创建函数(构造函数)。
与其用一张链表网,不如用一个字典保存起来,键是类名字符串,值是对应的构造函数。看起来很简单的样子,确实也很简单额。只是实现起来有些trick,比如字典一般是类的静态成员等。。。假设只有无参数的构造函数,首先定义上面所述的内容,类的字典。
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
| typedef void* (*CreateFuntion)(void);
class CClassFactory { public: CClassFactory() {} ~CClassFactory() {}
public: static void* GetClassByName(std::string name) { std::map<std::string, CreateFuntion>::const_iterator find; find = m_clsMap.find(name);
if(find==m_clsMap.end()) {
return NULL; } else { return find->second(); }
}
static void RegistClass(std::string name, CreateFuntion method) { m_clsMap.insert(std::make_pair(name,method)); }
private: static std::map<std::string,CreateFuntion> m_clsMap; };
std::map<std::string, CreateFuntion> CClassFactory::m_clsMap;
|
到目前为止,使用CClassFactory即可完成工作了。代码实在过于简单,不用解释了。下面看看一些带trick的东西。
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
| class RegistyClass {
public: RegistyClass(std::string name, CreateFuntion method) { CClassFactory::RegistClass(name, method); } };
template<class T, const char* name> class Register { public: Register() { const RegistyClass tmp = rc; }
static void* CreateInstance() { return new T; }
public: static const RegistyClass rc; };
template<class T, const char* name> const RegistyClass Register<T, name>::rc(name, Register<T, name>::CreateInstance);
#define REGISTRY_CLASS(class_name) \ extern char const array_##class_name[] = #class_name;\ Register<class_name, array_##class_name> register_class_name
|
RegistyClass不用说了,模板类Register是关键。模板T代表要注册的类类型,常量字符串模板name代表类名。其中的static成员rc用于注册当前类。注意CreateInstance的实现,说明只能调用无参数构造函数。至于Register构造函数里面的那句话也是不能省略的。
最麻烦的是下面的那个REGISTRYCLASS宏,其余代码都来自网络,这个宏确是我修改实现的。class_name代表类,比如CMesh,注意不是类名。这里有几个关键点,一个是extern。在vs2010中,不加extern修饰无法编译通过,具体原因估计是编译器支持不够吧。另一个是#class_name
,是把CMesh转换为”CMesh”。最最关键的是`array##class_name`,是生成array_CMesh的变量名。为什么要这么做了,这样注册不同的类就是不同的变量名,避免了变量重定义,否则会链接失败。使用方法是在要注册的类源文件中,包含ClassFactory.h头文件,再添加宏REGISTRY_CLASS(CMesh);即可。
网上提供的代码,还定义了声明类的宏,表示继承Register模板类自动支持反射机制。相关代码经过修改如下所示,这个就没有去具体使用了,因为修改类定义比较麻烦,添加一句宏到现在代码中却是很方便的事情。这个反射机制已经用到我现在的项目中了,实现了一个从配置文件读取信息,选择创建不同的界面的功能。
1 2 3 4 5 6 7
| #define DEFINE_CLASS(class_name)\ extern char char array_##class_name[]=#class_name;\ class class_name:public Register<class_name, NameArray>
#define DEFINE_CLASS_EX(class_name, father_class) \ extern char char array_##class_name[] = #class_name;\ class class_name: public Register<class_name, NameArray>, public father_class
|