写在前面:首先声明,C++实际上是不可以动态创建类的实例的。
下面简单做一个解释,所谓动态创建类的实例是指在程序运行过程中创建并使用一个“未知”的类。而“未知”是指在程序编译时并不知道有哪些类是需要动态创建的。对于C++这门语言来说,编译时不知道的类是不可以在运行时使用的。所以我说C++是不可以的。
不过C++可以做到另一件事情,基本可以满足大多数类似的需求。
我描述为通过类名称创建类的实例。
进入正题。
首先描述一下需求:
编写一个程序实现在程序运行过程中通过类名称(即字符串变量)创建该类的实例,且在主程序中不出现该类的声明。
然后进行分析设计:
1.在主程序中不出现类的声明,还需要用这个类。那很自然就会想到用基类指针实现。
2.创建一个指向该类的基类指针,则需要使用new运算符。
3.通过类名调用相应的new运算符,则需要使用map加函数指针。
4.最后,如何在主函数执行之前构造该map,则需要用到静态成员在主函数运行前进行创建的机制。
下面给出代码实现:
首先有一个CObject类作为基类,由它派生出CObjectA和CObjectB两个需要动态创建的子类。
Object.h
1 #ifndef __C_OBJECT_H_ 2 #define __C_OBJECT_H_ 3 4 #include "ObjectFactory.h" 5 6 class CObject 7 { 8 public: 9 CObject(): className("CObject") {} 10 virtual ~CObject(){} 11 virtual const std::string GetClassName() 12 { 13 return className; 14 } 15 private: 16 std::string className; 17 }; 18 19 #endif //__C_OBJECT_H_
ObjectA.h
1 #ifndef __C_OBJECT_A_H_ 2 #define __C_OBJECT_A_H_ 3 4 #include "Object.h" 5 6 class CObjectA : public CObject 7 { 8 public: 9 CObjectA(): className("CObjectA") {} 10 ~CObjectA(){} 11 const std::string GetClassName() 12 { 13 return className; 14 } 15 private: 16 std::string className; 17 }; 18 19 REGISTER_CLASS(CObjectA); 20 21 #endif //__C_OBJECT_A_H_
ObjectB.h
1 #ifndef __C_OBJECT_B_H_ 2 #define __C_OBJECT_B_H_ 3 4 #include "Object.h" 5 6 class CObjectB : public CObject 7 { 8 public: 9 CObjectB(): className("CObjectB") {} 10 ~CObjectB(){} 11 const std::string GetClassName() 12 { 13 return className; 14 } 15 private: 16 std::string className; 17 }; 18 19 REGISTER_CLASS(CObjectB); 20 21 #endif //__C_OBJECT_B_H_
然后重点就来了,上面两个类里面都有的宏定义REGISTER_CLASS。实际上就是声明一个带有静态成员的注册类,通过初始化该静态成员实现构造动态创建map。
ObjectFactory.h
1 #ifndef __C_OBJECT_FACTORY_H_ 2 #define __C_OBJECT_FACTORY_H_ 3 4 #include <map> 5 #include <string> 6 7 typedef void* (*NewInstancePt)(); 8 9 class CObjectFactory 10 { 11 public: 12 static void* CreateObject(const char *className) 13 { 14 std::map<std::string, NewInstancePt>::const_iterator it; 15 it = dynCreateMap.find(className); 16 if(it == dynCreateMap.end()) 17 return NULL; 18 else 19 { 20 NewInstancePt np = it->second; 21 return np(); 22 } 23 } 24 25 static void RegisterClass(const char *className, NewInstancePt np) 26 { 27 dynCreateMap[className] = np; 28 } 29 private: 30 static std::map<std::string, NewInstancePt> dynCreateMap; 31 }; 32 33 std::map<std::string, NewInstancePt> CObjectFactory::dynCreateMap; 34 35 class Register 36 { 37 public: 38 Register(const char *className, NewInstancePt np) 39 { 40 CObjectFactory::RegisterClass(className, np); 41 } 42 }; 43 44 #define REGISTER_CLASS(class_name) \ 45 class class_name##Register \ 46 { \ 47 public: \ 48 static void* NewInstance() \ 49 { \ 50 return new class_name(); \ 51 } \ 52 private: \ 53 static Register reg; \ 54 }; \ 55 Register class_name##Register::reg(#class_name, class_name##Register::NewInstance) 56 57 #endif //__C_OBJECT_FACTORY_H_
最后,当然还有如何使用的主函数内容。
RTTI.cpp
1 #include <stdio.h> 2 #include "ObjectFactory.h" 3 #include "Object.h" 4 #include "ObjectA.h" 5 #include "ObjectB.h" 6 7 int main(int argc, const char *argv[]) 8 { 9 CObject *objA = static_cast<CObject *>(CObjectFactory::CreateObject("CObjectA")); 10 std::string className; 11 if(objA == NULL) 12 { 13 printf("[ERROR] Can't Create Class ObjectA!\n"); 14 } 15 else 16 { 17 className = objA->GetClassName(); 18 printf("[OK] Create %s !\n", className.c_str()); 19 } 20 21 CObject *objB = static_cast<CObject *>(CObjectFactory::CreateObject("CObjectB")); 22 if(objB == NULL) 23 { 24 printf("[ERROR] Can't Create Class ObjectB!\n"); 25 } 26 else 27 { 28 className = objB->GetClassName(); 29 printf("[OK] Create %s !\n", className.c_str()); 30 } 31 32 return 0; 33 }
这样就完成了所谓的动态创建。这里需要注意的是在主函数所在的文件里面需要include所有的需要动态创建的类,否则的话在编译的过程中这些类以及其注册类就根本不会被编译,也就不会构建动态创建map,整个机制也就失效了。这也就是我说的C++实际上是不可以动态创建类的原因。
不得不说,如果想要彻底的动态创建,建议使用Java。简单看看Java的反射机制和ClassLoader就会知道原来动态加载如此容易。。。