分类:
C++
2011-12-29 22:24
235人阅读
收藏
举报
MFC的CRuntimeClass利用链表实现了C++类的动态创建。但是如果项目中对动态创建的要求比较低,我们完全可以利用map实现简单的动态创建。以下三个文件做了一个简单的实现。
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef BASE_H_
- #define BASE_H_
-
- #include "dyncreate.h"
-
- class base
- {
- public:
- typedef base* (*class_creator)();
- static base* create(const std::string& class_name);
- static std::map<const std::string,class_creator> class_set;
- struct _auto_register
- {
- _auto_register(const std::string& name, base::class_creator creator){
- base::class_set.insert(std::pair<const std::string,base::class_creator>(name,creator));}
- };
- public:
- base(void);
- virtual ~base(void);
-
- virtual void run();
- };
-
- #endif</span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #include <iostream>
- #include "base.h"
-
- std::map<const std::string,base::class_creator> base::class_set;
-
- base* base::create(const std::string& class_name)
- {
- std::map<const std::string,base::class_creator>::iterator it;
- it = class_set.find(class_name);
- if(it != class_set.end()){
- return (it->second)();
- }
- return NULL;
- }
-
- base::base(void){
- std::cout<<"base constructor called"<<std::endl;
- }
-
- base::~base(void){
- std::cout<<"base destructor called"<<std::endl;
- }
-
- void base::run(){
- std::cout<<"base class run"<<std::endl;
- }
- </span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef DERIVED_H_
- #define DERIVED_H_
-
- #include "base.h"
-
- class derived :
- public base
- {
- public:
- static base* create(){
- return (base*)(new derived);
- }
- public:
- derived(void);
- virtual ~derived(void);
-
- void run();
- };
-
- #endif</span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #include <iostream>
- #include "derived.h"
-
- static derived::_auto_register _register_derived("derived",derived::create);
-
- derived::derived(void){
- std::cout<<"derived constructor called"<<std::endl;
- }
-
- derived::~derived(void){
- std::cout<<"derived destructor called"<<std::endl;
- }</span>
- <span style="font-family:'Courier New';">
- void derived::run(){
- std::cout<<"derived class run"<<std::endl;
- }</span>
上面这些看起来有点别扭,我们把有关动态创建的代码全部分宏进行定义:
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef DYNCREATE_H_
- #define DYNCREATE_H_
-
- #include <map>
- #include <string>
-
- #define DECLARE_DYNBASE(base) \
- public: \
- typedef base* (*class_creator)(); \
- static base* create(const std::string& class_name); \
- static std::map<const std::string,class_creator> class_set; \
- struct _auto_register { \
- _auto_register(const std::string& name, base::class_creator creator){ \
- base::class_set.insert(std::pair<const std::string,base::class_creator>(name,creator));}};
-
- #define IMPLEMENT_DYNBASE(base) \
- std::map<const std::string,base::class_creator> base::class_set; \
- base* base::create(const std::string& class_name){ \
- std::map<const std::string,base::class_creator>::iterator it; \
- it = class_set.find(class_name); \
- if(it != class_set.end()){ \
- return (it->second)();} \
- return NULL; }
-
- #define DECLARE_DYNCREATE(derived,base) \
- public: \
- static base* create(){ \
- return (base*)(new derived); }
-
- #define IMPLEMENT_DYNCREATE(derived) \
- static derived::_auto_register _register_##derived(#derived,derived::create);
-
- #endif</span>
有了这些宏,base.h, base.cpp, derived.h和derived.cpp就可以变成以下这样子。
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef BASE_H_
- #define BASE_H_
-
- #include "dyncreate.h"
-
- class base
- {
- DECLARE_DYNBASE(base)
- DECLARE_DYNCREATE(base,base)
- public:
- base(void);
- virtual ~base(void);
-
- virtual void run();
- };
-
- #endif</span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #include <iostream>
- #include "base.h"
-
- IMPLEMENT_DYNBASE(base)
- IMPLEMENT_DYNCREATE(base)
-
- base::base(void){
- std::cout<<"base constructor called"<<std::endl;
- }
-
- base::~base(void){
- std::cout<<"base destructor called"<<std::endl;
- }
-
- void base::run(){
- std::cout<<"base class run"<<std::endl;
- }
- </span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef DERIVED_H_
- #define DERIVED_H_
-
- #include "base.h"
-
- class derived :
- public base
- {
- DECLARE_DYNCREATE(derived,base)
- public:
- derived(void);
- virtual ~derived(void);
-
- void run();
- };
-
- #endif</span>
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #include <iostream>
- #include "derived.h"
-
- IMPLEMENT_DYNCREATE(derived)
-
- derived::derived(void){
- std::cout<<"derived constructor called"<<std::endl;
- }
-
- derived::~derived(void){
- std::cout<<"derived destructor called"<<std::endl;
- }
-
- void derived::run(){
- std::cout<<"derived class run"<<std::endl;
- }</span>
这样子不但看起来简洁,使用起来也更加方便。
以上实现还存在一个问题。由于基类中存在静态的class_set成员,子类的cpp文件中也存在静态的_auto_register对像,且两个静成变
量分布在不同的文件中。编译器对静态变量的初始化顺序是不确定的。如果静态_auto_register对像的构造函数被自动调用,也就是需要往
class_set中添子类的时候,class_set还没有被创建或者初始化,那么就会出现错误。对此的解决办法是将class_set放入一个静态成
员函数get_class_set中,以保证第一次调用get_class_set的时候初始化class_set。
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #ifndef DYNCREATE_H_
- #define DYNCREATE_H_
-
- #include <map>
- #include <string>
-
- #define DECLARE_DYNBASE(base) \
- public: \
- typedef base* (*class_creator)(); \
- static base* create(const std::string& class_name); \
- <strong>static std::map<const std::string,class_creator>& get_class_set(){ \
- static std::map<const std::string,class_creator> class_set; \
- return class_set;} \</strong>
- struct _auto_register { \
- _auto_register(const std::string& name, base::class_creator creator){ \
- base::<strong>get_class_set()</strong>.insert(std::pair<const std::string,base::class_creator>(name,creator));}};
-
- #define IMPLEMENT_DYNBASE(base) \
- base* base::create(const std::string& class_name){ \
- std::map<const std::string,base::class_creator>::iterator it; \
- it = <strong>get_class_set()</strong>.find(class_name); \
- if(it != <strong>get_class_set()</strong>.end()){ \
- return (it->second)();} \
- return NULL; }
-
- #define DECLARE_DYNCREATE(derived,base) \
- public: \
- static base* create(){ \
- return (base*)(new derived); }
-
- #define IMPLEMENT_DYNCREATE(derived) \
- static derived::_auto_register _register_##derived(#derived,derived::create);
-
- #endif</span>
编写测试文件
- <span style="font-family:'Courier New';">
-
-
-
-
-
- #include "derived.h"
-
- int main()
- {
- base* ptr;
-
- ptr = base::create("derived");
- ptr->run();
- delete ptr;
-
- ptr = base::create("base");
- ptr->run();
- delete ptr;
-
- return 0;
- }</span>
输出
- <span style="font-family:'Courier New';">base constructor called
- derived constructor called
- derived class run
- derived destructor called
- base destructor called
- base constructor called
- base class run
- 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对象都已经创建?期待高手指点。。。
|