c++ so 反射_c++实现反射类

本文介绍了如何在C++中实现反射功能,利用工厂模式和__attribute__((constructor))关键字,创建对象实例。文章通过示例展示了如何在运行时通过类名动态创建对象,同时讨论了全局变量初始化、工厂类、宏定义等问题,并提供了解决方案。
摘要由CSDN通过智能技术生成

Any GetInstanceByName(const string& class_name);

返回值为Any,因为不知道返回值究竟是什么类型,所以假定可以返回任何类型,这里的Any使用的是Boost中的Any。该方法中需要new一个类型为class_name的对象返回,那么应该如何new该对象呢?借用上面使用工厂方法的经验,可以进一步使用工厂类,对于每个类,都有一个相应的工厂类ObjectFactoryClassName,由该工厂类负责生成相应的对象(为什么要使用工厂类?后面再作简单介绍)。

有了工厂类,也需要将类名与工厂类对应起来,对应方式可以使用map object_factory_map,object_factory_map负责从类名到相应工厂类的映射,这样,就可以通过类的名字找到对应ObjectFactory,然后使用ObjectFactory生成相应的对象。但是如何将相应的工厂类添加到object_factory_map中去呢,我们需要在定义新类的时候就将对应的工厂类添加到object_factory_map中,这里需要一个函数负责添加工厂类到object_factory_map中去(为什么需要一个函数负责?最后作简单说明)。

负责将新类对应的工厂类添加到全局变量object_factory_map的函数必须在使用object_factory_map之前执行。gcc中有一个关键字__attribute__((constructor)) ,使用该关键字声明的函数就可以在main函数之前执行。到现在,程序的结构类似这样:

// 负责实现反射的文件reflector.h:

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;

}

#define REFLECTOR(name) \

class ObjectFactory##name { \

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();

// 调用文件test.cc

class TestClass {

public:

void Out() {

cout << "i am TestClass" << endl;

}

};

REFLECTOR(TestClass);

// main函数

int main() {

Any instance = GetInstanceByName("TestClass");

TestClass* test_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()); \

} \

};

这里就不再详细讲修改后的代码了,有兴趣的朋友可以自己实现。

注:

至于上面为什么需要使用工厂类,而不是直接new一个对应的对象返回,原因是直接new是不可以的。例如如下定义:

#define REFLECT(name) \

Any GetInstanceByName(const string& class_name) {

return Any(new name);

}

如果是多个类使用的话,那么就会出现多个函数的定义。如果也借助工厂类的实现,如下实现:

#define REFLECT(name) \

Any GetInstanceByName##name(const string& class_name) {

return Any(new name);

}

这样是不会出现重复定义了,但是这样在生产新的对象时需要指定特定的函数,这不又回到原点了吗?因此工厂类充当的是个中介的角色,我们可以保存工厂类,然后根据名称寻找特定的工厂类来生成对应的对象。

注:

为什么需要使用函数添加工厂类?因为在程序中,全局空间中只能是变量的声明和定义,而不能是语句,例如:

可以这样写:

int a = 10;

int main() {}

但是不能这样写:

int a;

a = 10;

int main() {}

需要注意的知识点:

工厂模式;

全局变量的定义需要注意,不能定义在头文件中(当如,如果经过特殊处理,例如使用#ifndef保护另说);

Any类型的实现;(准备写另外一篇文章来探讨其实现细节)

宏的定义以及使用;(基本覆盖了宏的所有知识)

全局变量构造函数与__attribute__((constructor))的执行顺序;(调试了很久)

__attribute__((constructor))的问题;(编译器有关,放在函数定义前或定义后)

全局空间只能是声明或者定义,不能是语句;

static在函数中的使用;

全局变量类的定义与使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值