C++实现反射机制

来源:互联网 发布:成都软件开发工资 编辑:程序博客网 时间:2024/06/04 18:34

话不多说,先上段代码的运行结果:

class TestA:public Object{DECLARE_CLASS()public:TestA() {std::cout << "TestA constructor" << std::endl;}~TestA(){std::cout << "TestA destructor" << std::endl;}};IMPLEMENT_CLASS("testA_interface", TestA)int main()  {  Object* obj = Object::CreateObject("testA_interface");    delete obj;return 0;  } 

关键就一个功能——根据字符串创建了类对象

开发中经常会遇到根据不同借口创建不同类实例的情况,一般的做法就是if......else if...else if这样一直判断。比如说接口名是testA_interface,就创建一个TestA对象。如果接口不多还好办,如果接口有成千上万呢?维护起来都难办。那么有没有一种简单的方法?下面慢慢探究。

先说一下一般的思路:

常规的思路就是说建立一个map表,以接口名未key,构造函数为value。但有个问题——我们没办法定义一个构造函数的函数指针!

虽然用map的方法不可行,但这确是一个稚形,以这个为基础,一步一步解决问题(如果是C语言,这里没法用map,其实顶一个结构体就可以了)。

问题一:没法定义构造函数的函数指针

直接从构造函数指针不行,那就加一个中间层,没有什么是不能通过加一层来解决的,如果有,那就加两层。具体的做法就是map中不存构造函数了,而是存另一个类的对象,类的定义如下:

class ClassInfo{public:    ClassInfo(){}    ClassInfo(const std::string &interface_name, ObjectConstructorFn ctor)    {        m_interface_name = interface_name;        m_object_constructor = ctor;        Object::Register(this);    }    Object *CreateObject()const    {        return m_object_constructor ? (*m_object_constructor)() : 0;     }public:std::string          m_interface_name;ObjectConstructorFn  m_object_constructor;}; 

明显,我们把接口名跟构造函数聚合到了一个结构里面,ObjectConstructorFn是定义的一个函数指针,指向了每个类的构造函数。其实这个地方我们把原来map中的key和value一起当做value了。由此我们可以定义一个map

std::map<std::string, ClassInfo *> *class_info_map = new std::map<std::string, ClassInfo*>();
在调用的时候只需要find()->CreateObject()就可以了。

问题二:接口类的管理

就算这个问题不放这里我们也知道,各个接口对应的类它们肯定有一个公有的基类,其次这个地方恩把所有类继承一公有基类也方便我们管理。所有最简基类的定义如下:

class Object{public:      Object(){}    virtual ~Object(){}    static void Register(ClassInfo* ci);    static Object* CreateObject(const std::string &interface_name);};
关键成员函数有两个,register和createobject,本来是想把这个类设置成单例的,也就是说构造函数应该设为private,但是由于这个类是基类,在创建子类对象的时候会调用基类的构造函数,所以这儿就设成了public,如果createobject来创建对象。而对于register成员函数,它就是根据接口把ClassInfo插入到class_info_map,最简单粗暴的方法就是class_info_map[ci->interface_name] = ci。由前面ClassInfo定义可知,ci已经保存了接口名返回实例的函数。

    问题三:子类的实现

        目前为止其他都可以了,就差子类的实现了,前面说了,ClassInfo标示了一个接口名和接口的对应关系。所以子类需要包含一个ClassInfo的成员变量。定义两个宏:

#define DECLARE_CLASS() \    protected: \        static ClassInfo ms_classinfo; \    public:  \        static Object* CreateObject();  #define IMPLEMENT_CLASS(interface_name, class_name)            \ClassInfo class_name::ms_classinfo(interface_name,(ObjectConstructorFn)class_name::CreateObject);\        Object* class_name::CreateObject() \            { return new class_name;} 
    这两个宏定义了所有子类需要实现的部分,所以只要直接引入这两个宏就可以了,在宏中定义了唯一标示接口名与类对应关系的ms_classinfo变量,此外有实现了返回每一个类型对象的CreateObject函数。

    下面贴一下完整的代码

#include <iostream>#include <string>#include <map>using namespace std;#define DECLARE_CLASS() \    protected: \        static ClassInfo ms_classinfo; \    public:  \        static Object* CreateObject();  #define IMPLEMENT_CLASS(interface_name, class_name)            \ClassInfo class_name::ms_classinfo(interface_name,(ObjectConstructorFn)class_name::CreateObject);\    Object* class_name::CreateObject() \        { return new class_name;} class ClassInfo;class Object;typedef Object* (*ObjectConstructorFn)();class Object  { protected:    Object(){}public:      virtual ~Object(){}    static void Register(ClassInfo* ci);             static Object* CreateObject(std::string name);         static std::map<std::string, ClassInfo *> *classInfoMap;};  class ClassInfo  {  public:  ClassInfo(const std::string className, ObjectConstructorFn ctor);ClassInfo();Object *CreateObject()const;public:  std::string m_className;ObjectConstructorFn m_objectConstructor;}; std::map<std::string, ClassInfo *> *Object::classInfoMap = new std::map<std::string, ClassInfo*>();void Object::Register(ClassInfo* ci)  {   if (NULL != ci && classInfoMap->find(ci->m_className) == classInfoMap->end()){  classInfoMap->insert(std::map<std::string, ClassInfo*>::value_type(ci->m_className, ci)); }  }Object* Object::CreateObject(std::string name)  {std::map<std::string, ClassInfo*>::const_iterator iter = classInfoMap->find(name);  if (iter != classInfoMap->end()){return iter->second->CreateObject();}return NULL;  }  ClassInfo::ClassInfo(const std::string className, ObjectConstructorFn ctor):m_className(className), m_objectConstructor(ctor)  {  Object::Register(this);}ClassInfo::ClassInfo(){}Object *ClassInfo::CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : 0; }class Test:public Object{        DECLARE_CLASS()public:        Test(){cout << "Test constructor" << endl;}        ~Test(){cout << "Test destructor" << endl;}};IMPLEMENT_CLASS("xxxx", Test)int main()  {    Object* obj = Object::CreateObject("xxxx");    delete obj;    return 0;  }  
   熟悉MFC的同学应该知道,MFC中有一个BEGIN_MESSAGE_MAP,它也是实现了不同消息ID与不同处理函数的映射,实现原理和这个类似。


    

0 0
原创粉丝点击