您的位置:首页 > 编程语言 > C语言/C++

C++实现反射机制

2016-11-29 18:17 447 查看
话不多说,先上段代码的运行结果:

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与不同处理函数的映射,实现原理和这个类似。

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息