c++实现反射类(二)
t_class = instance.any_cast();
return 0;
}
到这里还有一个问题,全局变量ObjectFactoryMap是不能放在头文件中的,因为如果多个类包含该头文件时,就会出现重复定义的错误,是编译不过的。因此,将该变量放在其
源码reflector.cc文件中:
// reflector.h,包含声明:
extern map object_factory_map;
Any GetInstanceByName(const string& name);
// reflector.cc:
map object_factory_map;
Any GetInstanceByName(const string& name) {
if (object_factory_map.find(name) != object_factory_map.end()) {
return object_factory_map[name]->NewInstance();
}
return NULL;
}
上述程序编译能够通过,但是运行时出错,后来定位到是在使用全局变量object_factory_map时出错,经过调试了很久,在网上查相应的资料也没找到。经过不停的尝试,才发现原来是全局变量object_factory_map没有初始化,在仔细的测试了以后发现,是__attribute__((constructor))与全局变量类构造函数的执行顺序的问题,一般全局变量是在__attribute__(constructor)前完成初始化的,但是如果__attribute__是在main函数所在的文件,而全局变量是在其他文件定义的,那么__attribute__(constructor)就会在全局变量类构造函数前面执行,这样,上面的程序在全局变量类还没有完成初始化,也就是还没有执行构造函数,就在__attribute__(constructor)声明的函数中进行了使用,因此会出现问题。不过,在执行__attribute__时已经看到了全局变量的定义,只是没有执行全局变量的构造函数(这里,如果全局变量不是类,而是普通类型,是没有问题的)。所以,程序的结构还需要进一步修改。
现在解决如何定义和使用全局变量object_factory_map的问题。既然我们不能直接使用该变量,那么可以通过显示调用函数来返回该变量,如果直接在函数中new一个对象返回的话,那么每次调用都会new一个新的对象,而我们全局只需要一个该对象,这时该是static出现的时候了。我们可以这样定义:
// reflector.cc
map& object_factory_map() {
static map* factory_map = new map;
return *factory_map;
}
这样定义还有另外一个优点,程序只是在真正需要调用g_objectfactory_map时才会生成相应的对象,而如果程序没有调用,也不会生成对应的对象。当然,在这里new一个对象的代价不大,但是如果new的对象非常耗时的话,这种使用函数中static变量代替全局变量方法的优势就非常明显了。到现在反射程序变成如下这样:
// 负责实现反射的文件reflector.h:
// 工厂类的基类
class ObjectFactory {
public:
virtual Any NewInstance() {
return Any();
}
};
map& object_factory_map();
Any GetInstanceByName(const string& name);
#define REFLECTOR(name) \
class ObjectFactory##name : public ObjectFactory { \
public: \
Any NewInstance() { \
return Any(new name); \
} \
}; \
void register_factory_##name() { \
if (object_factory_map().find(#name) == object_factory_map().end()) { \
object_factory_map()[#name] = new ObjectFactory##name(); \
} \
} \
__attribute__(constructor)void register_factory##name()
// reflector.cc
map& object_factory_map() {
static map* factory_map = new map;
return *factory_map;
}
Any GetInstanceByName(const string& name) {
if (object_factory_map().find(name) != object_factory_map().end()) {
return object_factory_map()[name]->NewInstance();
}
return NULL;
}
到现在接近尾声了,不过在很多时候,我们都是在已有基类的基础上添加新的类,就好比上述网页识别的程序,各个识别策略类都继承共同的基类,这样,我们可以进一步修改反射程序,将GetInstanceByName放在另外一个类中,返回的是基类的指针,因此在定义基类时也需要注册一个宏,如下所示,同时需要修改objector_factory_map的结构为map >,第一个key是基类的名字,第二map中的key是生成类的名字,基类宏的定义类似如下:
#define REFLECTOR_BASE(base_class) \
class base_class##Reflector { \
public: \
static base_class* GetInstanceByName(const string& name) { \
map& map = object_factory_map()[#base_class]; \
map::iterator iter = map.find(name); \
if (iter == map.end()) { \
return NULL; \
} \
Any object = iter->second->NewInstance(); \
return *(object.any_cast()); \
} \
};
这里就不再详细讲修改后的代码了,有兴趣的朋友可以自己实现。
注:
至于上面为什么需要使用工厂类,而