寫在前面:首先聲明,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就會知道原來動態加載如此容易。。。