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 }
到此,一个简单的反射机制+工厂模式的功能模块就实现了。