找回密码
 立即注册
  • QQ空间
  • 回复
  • 收藏

c++可以实现反射么?YES

admin 2019-11-25 16:42 106人围观 C++相关

一.前言:

JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别)。但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。

ClassT* obj = FactoryCreate("ClassT");

类似于以上的语法,虽然C++没有自带的语法可以实现,但是我们可以自己通过其他方法来实现。

二.实现:

使用简单工厂模式


我们很容易可以想到可以使用简单工厂模式来实现这个效果:比如
classObject
{
public:
    virtualstringToString() =0;
};
这个是所有需要实现反射机制的类需要继承的基类,然后派生出来的类只需要再实现这个ToString即可。例如:
classMyClass :publicObject
{
public:
    virtualstringToString(){ return"MyClass"; }
};
然后就是用于产生对象的工厂。
Object*FactoryCreat(conststring&className)
{
    if (className=="ClassA")
        returnnewClassA;
    elseif (className=="ClassB")
        returnnewClassB;
    elseif(className=="ClassC")
        returnnewClassC;
    elseif(className=="ClassD")
        returnnewClassD;
    elseif(className=="ClassE")
        returnnewClassE;
...
}
我们使用就可以这样:
intmain()
{
    Object*obj=FactoryCreat("MyClass");
    cout<<obj->ToString();
    deleteobj;
    return0;
}
我们使用简单工厂模式感觉好像是解决了问题,可以实现用字符串去new一个对应的对象,但是假如我们要新建一个类或者修改一个类,那么这个FactoryCreat都要进行修改。十分不利于维护。所以我们需要换一个方式来处理。

工厂模式结合回调机制。


首先我们要梳理一下这个方法的基本脉络:

  • 工厂内部需要有个映射,也就是一个字符串对应一个类new的方法。

  • 工厂给出一个接口,我们传入字符串,那么返回这个字符串对应的方法new出来的对象指针。

  • 我们新建的类,如果需要支持反射机制,那么这个类需要自动将自己的new方法和名字注册到工厂的映射中。

OK,如果我们能完成以上几个要求,那么我们在类进行拓展的时候需要改动的地方就十分少了。对于工厂的代码我们基本上是不会改变的。也就基本上实现了我们C++反射机制的基本功能。

下面我们来一步一步解析代码:首先我们还是需要一个Object作为需要支持反射机制类的基类
//Reflex.h
classObject
{
public:
    Object(){}
    virtual~Object(){}
    staticboolRegister(ClassInfo*ci);          //注册传入一个classInfo(类信息),将这个类的信息注册到映射中
    staticObject*CreateObject(stringname);     //工厂生产对象的接口
};
然后是实现:
//Reflex.cpp
staticstd::map<string, ClassInfo*>*classInfoMap=NULL;
boolObject::Register(ClassInfo*ci)
{
    if (!classInfoMap)   {
        classInfoMap=newstd::map<string, ClassInfo*>();      //这里我们是通过map来存储这个映射的。
    }
    if (ci) {
        if (classInfoMap->find(ci->m_className) ==classInfoMap->end()){
            classInfoMap->insert(std::map<string, ClassInfo*>::value_type(ci->m_className, ci)); // 类名 <-> classInfo
        }
    }
    returntrue;
}
Object*Object::CreateObject(std::stringname)
{
    std::map<string, ClassInfo*>::const_iteratoriter=classInfoMap->find(name);
    if (classInfoMap->end() !=iter) {
        returniter->second->CreateObject();         //当传入字符串name后,通过name找到info,然后调用对应的CreatObject()即可
    }
    returnNULL;
}
剩下的我们还需要一个classinfo类就大功告成了:
//Reflex.h

typedefObject* (*ObjectConstructorFn)(void);
classClassInfo
{
public:
    ClassInfo(conststd::stringclassName, ObjectConstructorFnctor)
    :m_className(className), m_objectConstructor(ctor)
    {
        Object::Register(this);             //classInfo的构造函数是传入类名和类对应的new函数然后自动注册进map中。
    }
    virtual~ClassInfo(){}
    Object*CreateObject()const { returnm_objectConstructor? (*m_objectConstructor)() : 0; }
    boolIsDynamic()const { returnNULL!=m_objectConstructor; }
    conststd::stringGetClassName()const { returnm_className; }
    ObjectConstructorFnGetConstructor()const{ returnm_objectConstructor; }
public:
    stringm_className;
    ObjectConstructorFnm_objectConstructor;
};
有了这些类后,我们只需要让需要支持反射的类满足以下要求即可:

  • 继承Object类。

  • 重载一个CreatObject()函数,里面 return  new 自身类。

  • 拥有一个classInfo的成员并且用类名和CreatObject初始化。

满足以上三个要求的类我们就可以利用反射机制来创建对象了。我们可以看下面的例子:
classB : publicObject
{
public:
    B(){ cout<<hex<< (long)this<<" B constructor!"<<endl; }
    ~B(){ cout<<hex<< (long)this<<" B destructor!"<<endl; }
    virtualClassInfo*GetClassInfo() const{ return&ms_classinfo; }
    staticObject*CreateObject() { returnnewB; }
protected:
    staticClassInfoms_classinfo;
};
ClassInfoB::ms_classinfo("B", B::CreateObject);
使用的话我们就只需要调用Object::CreatObject(string) 传入类名即可。
intmain()
{
    Object*obj=Object::CreateObject("B");
    deleteobj;
    return0;
}
基本上反射机制的功能就实现了,而且使用回调注册在后期拓展上也容易维护。

三.使用宏简化代码:


其实大家发现,因为我们要让类支持反射那么就要满足我们上面的那三个要求,但是每个类都要写这样相似的东西。仔细一看,包括函数申明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?有,结合MFC的类型名录网构建,我们也可以使用宏来优化我们的注册。
//Reflex.h

//类申明中添加 classInfo 属性 和 CreatObject、GetClassInfo 方法
#define DECLARE_CLASS(name) \
   protected: \
       static ClassInfo ms_classinfo; \
   public: \
       virtual ClassInfo* GetClassInfo() const; \
       static Object* CreateObject();

//实现CreatObject 和 GetClassInfo 的两个方法
#define IMPLEMENT_CLASS_COMMON(name,func) \
   ClassInfo name::ms_classinfo((#name), \
            (ObjectConstructorFn) func); \
                         \
   ClassInfo *name::GetClassInfo() const \
       {return &name::ms_classinfo;}

//classInfo 属性的初始化
#define IMPLEMENT_CLASS(name)           \
   IMPLEMENT_CLASS_COMMON(name,name::CreateObject) \
   Object* name::CreateObject()                   \
       { return new name;}
有了宏替换后,我们定义一个新的类。只需要在类定义中添加 DECLARE_CLASS(classname) 实现中添加IMPLEMENT_CLASS(classname)就可以让这个类实现反射了。例如我们上面的类B就可以这样写:
classB : publicObject
{
DECLARE_CLASS(B)
public:
    B(){ cout<<hex<< (long)this<<" B constructor!"<<endl; }
    ~B(){ cout<<hex<< (long)this<<" B destructor!"<<endl; }
};
IMPLEMENT_CLASS(B)
这样不管以后需要添加、修改什么功能都只需要修改宏就可以了而不需要每个类每个类去添加、修改方法。

ok到这里基本上,c++反射机制的实现就大功告成了!。


----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:小翔哥与cplusplus,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

yafeilinux和他的朋友们微信公众号二维码

微信公众号

专注于Qt嵌入式Linux开发等。扫一扫立即关注。

Qt开源社区官方QQ群二维码

QQ交流群

欢迎加入QQ群大家庭,一起讨论学习!

我有话说......