分享

利用map动态创建C 类对象

 quasiceo 2012-12-05

利用map动态创建C++类对象

分类: C++ 235人阅读 评论(0) 收藏 举报

          MFC的CRuntimeClass利用链表实现了C++类的动态创建。但是如果项目中对动态创建的要求比较低,我们完全可以利用map实现简单的动态创建。以下三个文件做了一个简单的实现。

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     base.h 
  5. */  
  6.   
  7. #ifndef BASE_H_  
  8. #define BASE_H_  
  9.   
  10. #include "dyncreate.h"  
  11.   
  12. class base  
  13. {  
  14. public:  
  15.     typedef base* (*class_creator)();                     // 声明类的创建函数指针  
  16.     static base* create(const std::string& class_name);   // 由类名创建类  
  17.     static std::map<const std::string,class_creator> class_set; // 保存类名与类创建函数  
  18.     struct _auto_register        // 用于自动注册  
  19.     {  
  20.         _auto_register(const std::string& name, base::class_creator creator){  
  21.         base::class_set.insert(std::pair<const std::string,base::class_creator>(name,creator));}  
  22.     };  
  23. public:  
  24.     base(void);  
  25.     virtual ~base(void);  
  26.   
  27.     virtual void run();  
  28. };  
  29.   
  30. #endif</span>  


  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     base.cpp 
  5. */  
  6.   
  7. #include <iostream>  
  8. #include "base.h"  
  9.   
  10. std::map<const std::string,base::class_creator> base::class_set;   
  11.   
  12. base* base::create(const std::string& class_name)    // 动态创建函数实现  
  13. {  
  14.     std::map<const std::string,base::class_creator>::iterator it;  
  15.     it = class_set.find(class_name); // 在map中查找类名  
  16.     if(it != class_set.end()){  
  17.         return (it->second)();  
  18.     }  
  19.     return NULL;  
  20. }  
  21.   
  22. base::base(void){  
  23.     std::cout<<"base constructor called"<<std::endl;  
  24. }  
  25.   
  26. base::~base(void){  
  27.     std::cout<<"base destructor called"<<std::endl;  
  28. }  
  29.   
  30. void base::run(){  
  31.     std::cout<<"base class run"<<std::endl;  
  32. }  
  33. </span>  

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     derived.h 
  5. */  
  6.   
  7. #ifndef DERIVED_H_  
  8. #define DERIVED_H_  
  9.   
  10. #include "base.h"  
  11.   
  12. class derived :  
  13.     public base  
  14. {  
  15. public:  
  16.     static base* create(){   // 类创建函数  
  17.         return (base*)(new derived);  
  18.     }  
  19. public:  
  20.     derived(void);  
  21.     virtual ~derived(void);  
  22.   
  23.     void run();  
  24. };  
  25.   
  26. #endif</span>  

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     derived.cpp 
  5. */  
  6.   
  7. #include <iostream>  
  8. #include "derived.h"  
  9.   
  10. static derived::_auto_register _register_derived("derived",derived::create); // 声明一个静态变量,自动调用它的构造函数对类进行自动注册  
  11.   
  12. derived::derived(void){  
  13.     std::cout<<"derived constructor called"<<std::endl;  
  14. }  
  15.   
  16. derived::~derived(void){  
  17.     std::cout<<"derived destructor called"<<std::endl;  
  18. }</span>  
  19. <span style="font-family:'Courier New';">  
  20. void derived::run(){  
  21.     std::cout<<"derived class run"<<std::endl;  
  22. }</span>  

上面这些看起来有点别扭,我们把有关动态创建的代码全部分宏进行定义:
  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     dyncreate.h 
  5. */  
  6.   
  7. #ifndef DYNCREATE_H_  
  8. #define DYNCREATE_H_  
  9.   
  10. #include <map>  
  11. #include <string>  
  12.   
  13. #define DECLARE_DYNBASE(base)   \  
  14. public: \  
  15.     typedef base* (*class_creator)(); \  
  16.     static base* create(const std::string& class_name); \  
  17.     static std::map<const std::string,class_creator> class_set; \  
  18.     struct _auto_register { \  
  19.         _auto_register(const std::string& name, base::class_creator creator){ \  
  20.         base::class_set.insert(std::pair<const std::string,base::class_creator>(name,creator));}};  
  21.   
  22. #define IMPLEMENT_DYNBASE(base) \  
  23.     std::map<const std::string,base::class_creator> base::class_set; \  
  24.     base* base::create(const std::string& class_name){ \  
  25.         std::map<const std::string,base::class_creator>::iterator it; \  
  26.         it = class_set.find(class_name); \  
  27.         if(it != class_set.end()){ \  
  28.             return (it->second)();} \  
  29.         return NULL; }  
  30.   
  31. #define DECLARE_DYNCREATE(derived,base) \  
  32. public: \  
  33.     static base* create(){ \  
  34.         return (base*)(new derived); }  
  35.   
  36. #define IMPLEMENT_DYNCREATE(derived) \  
  37.     static derived::_auto_register _register_##derived(#derived,derived::create);   
  38.   
  39. #endif</span>  

有了这些宏,base.h,  base.cpp, derived.h和derived.cpp就可以变成以下这样子。
  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     base.h 
  5. */  
  6.   
  7. #ifndef BASE_H_  
  8. #define BASE_H_  
  9.   
  10. #include "dyncreate.h"  
  11.   
  12. class base  
  13. {  
  14.     DECLARE_DYNBASE(base)            // 声明动态创建基类  
  15.     DECLARE_DYNCREATE(base,base)     // 基类也可以动态创建  
  16. public:  
  17.     base(void);  
  18.     virtual ~base(void);  
  19.   
  20.     virtual void run();  
  21. };  
  22.   
  23. #endif</span>  

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     base.cpp 
  5. */  
  6.   
  7. #include <iostream>  
  8. #include "base.h"  
  9.   
  10. IMPLEMENT_DYNBASE(base)     // 实现动态创建基类  
  11. IMPLEMENT_DYNCREATE(base)   // 实现基类的动态创建  
  12.   
  13. base::base(void){  
  14.     std::cout<<"base constructor called"<<std::endl;  
  15. }  
  16.   
  17. base::~base(void){  
  18.     std::cout<<"base destructor called"<<std::endl;  
  19. }  
  20.   
  21. void base::run(){  
  22.     std::cout<<"base class run"<<std::endl;  
  23. }  
  24. </span>  

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     derived.h 
  5. */  
  6.   
  7. #ifndef DERIVED_H_  
  8. #define DERIVED_H_  
  9.   
  10. #include "base.h"  
  11.   
  12. class derived :  
  13.     public base  
  14. {  
  15.     DECLARE_DYNCREATE(derived,base)  // 声明动态创建子类  
  16. public:  
  17.     derived(void);  
  18.     virtual ~derived(void);  
  19.   
  20.     void run();  
  21. };  
  22.   
  23. #endif</span>  


  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     derived.cpp 
  5. */  
  6.   
  7. #include <iostream>  
  8. #include "derived.h"  
  9.   
  10. IMPLEMENT_DYNCREATE(derived)  // 实现动态创建子类  
  11.   
  12. derived::derived(void){  
  13.     std::cout<<"derived constructor called"<<std::endl;  
  14. }  
  15.   
  16. derived::~derived(void){  
  17.     std::cout<<"derived destructor called"<<std::endl;  
  18. }  
  19.   
  20. void derived::run(){  
  21.     std::cout<<"derived class run"<<std::endl;  
  22. }</span>  
          这样子不但看起来简洁,使用起来也更加方便。

          以上实现还存在一个问题。由于基类中存在静态的class_set成员,子类的cpp文件中也存在静态的_auto_register对像,且两个静成变 量分布在不同的文件中。编译器对静态变量的初始化顺序是不确定的。如果静态_auto_register对像的构造函数被自动调用,也就是需要往 class_set中添子类的时候,class_set还没有被创建或者初始化,那么就会出现错误。对此的解决办法是将class_set放入一个静态成 员函数get_class_set中,以保证第一次调用get_class_set的时候初始化class_set。

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     dyncreate.h 
  5. */  
  6.   
  7. #ifndef DYNCREATE_H_  
  8. #define DYNCREATE_H_  
  9.   
  10. #include <map>  
  11. #include <string>  
  12.   
  13. #define DECLARE_DYNBASE(base)   \  
  14. public: \  
  15.     typedef base* (*class_creator)(); \  
  16.     static base* create(const std::string& class_name); \  
  17.     <strong>static std::map<const std::string,class_creator>& get_class_set(){ \  
  18.         static std::map<const std::string,class_creator> class_set; \  
  19.         return class_set;} \</strong>  
  20.     struct _auto_register { \  
  21.         _auto_register(const std::string& name, base::class_creator creator){ \  
  22.         base::<strong>get_class_set()</strong>.insert(std::pair<const std::string,base::class_creator>(name,creator));}};  
  23.   
  24. #define IMPLEMENT_DYNBASE(base) \  
  25.     base* base::create(const std::string& class_name){ \  
  26.         std::map<const std::string,base::class_creator>::iterator it; \  
  27.         it = <strong>get_class_set()</strong>.find(class_name); \  
  28.         if(it != <strong>get_class_set()</strong>.end()){ \  
  29.             return (it->second)();} \  
  30.         return NULL; }  
  31.   
  32. #define DECLARE_DYNCREATE(derived,base) \  
  33. public: \  
  34.     static base* create(){ \  
  35.         return (base*)(new derived); }  
  36.   
  37. #define IMPLEMENT_DYNCREATE(derived) \  
  38.     static derived::_auto_register _register_##derived(#derived,derived::create);   
  39.   
  40. #endif</span>  

       编写测试文件

  1. <span style="font-family:'Courier New';">/* 
  2. * Author:   yejingx 
  3. * Date:     2011-12-29 
  4. * File:     dyn_test.cpp 
  5. */  
  6.   
  7. #include "derived.h"  
  8.   
  9. int main()  
  10. {  
  11.     base* ptr;  
  12.   
  13.     ptr = base::create("derived");  
  14.     ptr->run();  
  15.     delete ptr;  
  16.   
  17.     ptr = base::create("base");  
  18.     ptr->run();  
  19.     delete ptr;  
  20.   
  21.     return 0;  
  22. }</span>  
输出
  1. <span style="font-family:'Courier New';">base constructor called  
  2. derived constructor called  
  3. derived class run  
  4. derived destructor called  
  5. base destructor called  
  6. base constructor called  
  7. base class run  
  8. base destructor called</span>  

        到此,我们已经保证在第一次使用class_set前对其进行创建并初始化,但是仍然存在一个问题。设想,假如我们编写的程序在 _auto_register对象初始化前调用了base::create(class_name),这时候的base::class_set是空 的,base::create(class_name)就返回一个空指针!有一次我把base, derived等类打包成一个lib,然后写了另外一个文件调用这个lib中的base::create,就出现了上述问题。之后我用了很土的办法临时解 决了这个问题,在此不提也罢。

       如何确保第一次调用base::create之前_auto_register对象都已经创建?期待高手指点。。。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多