C++实现反射机制

最近,做项目遇到需要使用反射机制和工厂模式,于是便总结一下。

  • 工厂模式

工厂模式算是一种很常用的设计模式了,应该都不陌生。假如需要运输货物,交通工具有汽车,火车,飞机,那么可以为每种交通工具都设计一个类,具体使用哪种交通工具时,只要实例化一个它的对象就可以。但是这样会产生很多冗余代码,也不方便代码可扩展。若是新增一个交通工具轮船,则又要新设计一个轮船类;如果交通工具模块与其他模块有交互,那么新增类别代码修改的地方会很多,也容易出错,牵一发动全身。而工厂模式可以解决这个问题。可以将创建实例和使用实例的工作分开,定义一个创建交通工具的接口,让子类自己决定实例哪一个交通工具。

 1 class Transportation
 2 {
 3 public:
 4     Transportation() {}
 5     ~Transportation() {}
 6     virtual void run() = 0;
 7 };
 8 class Plane :public Transportation
 9 {
10 public:
11     Plane() {}
12     ~Plane() {}
13     virtual void run()
14     {
15         cout << "I am plane" << endl;
16     }
17 };
18 class Train :public Transportation
19 {
20 public:
21     Train(){}
22     ~Train() {}
23     virtual void run()
24     {
25         cout << "I am train" << endl;
26     }
27 };
28 class TransportationFactory
29 {
30 public:
31     static Transportation* createTransportation(const string& name)
32     {
33         if (name == "火车")
34             return new Train;
35         else if (name == "飞机")
36             return new Plane;
37     }
38 };

如上面代码所示,我们可以通过调用createTransportation方法传入不同的参数来获取不同的交通工具对象。

1 int main()
2 {
3     TransportationFactory t;
4     Transportation* obj = (Transportation*)t.createTransportation("飞机");
5     obj->run();
6     return 0;
7 }

上面就是一个简单的工厂模式。但是如果新增类别,仍然需要修改createTransportation函数中的if-else语句来新增接口;并且每次调用都新建一个实例返回。如果可以根据传入的字符串直接获取对应类的实例就非常方便。反射机制就是用来实现这一需求的。

  • 反射机制

c++本身并不支持反射机制,但是可以实现一些简单的功能。

首先,需要设计一个保存类的信息以及创建类的实例的函数的数据结构,所以可以使用map,键名为类名,键值为创建对应类实例的函数名。

 1 typedef void* (*register_func)();
 2 
 3 class Store
 4 {
 5 public:
 6     static Store* getInstance();
 7     void* findInstance(const string& class_name);
 8     void registerStore(const string& class_name, register_func func);
 9 private:
10     static Store* instance;
11     map<string, register_func> m_register;
12 };
13 
14 Store* Store::instance = nullptr;
15 
16 Store* Store::getInstance()
17 {
18     if (!instance) instance = new Store;
19     return instance;
20 }
21 
22 void* Store::findInstance(const string& class_name)
23 {
24     map < string, register_func>::iterator it = m_register.find(class_name);
25     if (it == m_register.end())
26         return nullptr;
27     else
28         return it->second();
29 }
30 
31 void Store::registerInstance(const string& class_name, register_func func)
32 {
33     m_register[class_name] = func;
34 }

这里使用单例模式创建Store类,用于存储类名和类的创建实例的函数。没新增一个类别,就使用registerInstance函数存储类名和创建实例的函数地址存入 map对象中;需要获取时,通过findInstance查找。

然后再定义一个注册类Register,通过构造函数调用Store类的registerInstance函数。

 1 class Register
 2 {
 3 public:
 4     Register(string class_name, register_func func);
 5     ~Register();
 6 };
 7 
 8 Register::Register(string class_name, register_func func)
 9 {
10     Store::getInstance()->registerInstance(class_name, func);
11 }
12 
13 Register::~Register() {}

最后,每创建一个新类别的时候,通过宏定义创建一个Register实例,实现新类别的注册。

 1 #define REGISTER_CLASS(class_name) \
 2     class class_name##Register \
 3     { \
 4     public: \
 5         static void* newInstance() \
 6         { \
 7             return new class_name; \
 8         } \
 9     private: \
10         static const Register reg; \
11     }; \
12 const Register class_name##Register::reg(#class_name, class_name##Register::newInstance);

这里,在新增的交通工具类,比如Plane后面,使用REGISTER_CLASS(Plane)即可完成Plane类的实例注册。再修改TransportationFactory类的createTransportation函数如下:

1 class TransportationFactory
2 {
3 public:
4     static Transportation* createTransportation(const string& name)
5     {
6         return (Transportation*)Store::getInstance()->findInstance(name);
7     }
8 };

在主函数中调用Plane的实例的时候,只需要传入字符串就可以。

1 int main()
2 {
3     Transportation* plane = (Plane*)TransportationFactory::createTransportation("Plane");
4     if (plane == nullptr) cout << "failed" << endl;
5     else plane->run();
6     system("pause");
7     return 0;
8 }

到此,一个简单的反射机制+工厂模式的功能模块就实现了。

posted @ 2020-02-29 15:03  p_is_p  阅读(1353)  评论(0编辑  收藏  举报