C++ 實現反射機制


C++ 實現反射機制

一.前言:
JAVA有着一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。然而C++是不支持反射機制,雖然C++有RTTI(運行時類型識別)。但是想要實現C++對象序列化,序列化就是存儲到磁盤上,將對象變成一定格式的二進制編碼,然后要用的時候再將保存在磁盤上的二進制編碼轉化成一個內存中的對象,這個過程中總是需要有一個指示來告訴編譯器要生成什么樣的對象,最簡單的方式當然就是類名了,例如:將一個ClassXXX對象存儲到磁盤上,再從磁盤讀取的時候讓編譯器根據“ClassXXX”名稱來new一個對象。


ClassT* obj = FactoryCreate("ClassT");

類似於以上的語法,雖然C++沒有自帶的語法可以實現,但是我們可以自己通過其他方法來實現。(由於本人能力有限,所以該篇博客只是講解如何簡單的實現這個反射機制,而對C++中擁有這個反射機制是否有必要不做任何討論。當然,如果博客中有什么地方說的有錯誤還望大家可以在下面評論指出謝謝)

二.實現:
1.我們很容易可以想到可以使用簡單工廠模式來實現這個效果:比如
class Object
{
public:
    virtual string ToString() = 0;
};
這個是所有需要實現反射機制的類需要繼承的基類,然后派生出來的類只需要再實現這個ToString即可。例如:
class MyClass :public Object
{
public:
    virtual string ToString(){ return "MyClass"; }
};
然后就是用於產生對象的工廠。
Object* FactoryCreat(const string& className)
{
    if (className == "ClassA")
        return new ClassA;
    else if (className == "ClassB")
        return new ClassB;
    else if(className == "ClassC")
        return new ClassC;
    else if(className == "ClassD")
        return new ClassD;
    else if(className == "ClassE")
        return new ClassE;
    ...
}
我們使用就可以這樣:
int main()
{
    Object* obj = FactoryCreat("MyClass");
    cout << obj->ToString();
    delete obj;
    return 0;
}

我們使用簡單工廠模式感覺好像是解決了問題,可以實現用字符串去new一個對應的對象,但是假如我們要新建一個類或者修改一個類,那么這個FactoryCreat都要進行修改。十分不利於維護。所以我們需要換一個方式來處理。

2.工廠模式結合回調機制。

首先我們要梳理一下這個方法的基本脈絡:
1.工廠內部需要有個映射,也就是一個字符串對應一個類new的方法。
2.工廠給出一個接口,我們傳入字符串,那么返回這個字符串對應的方法new出來的對象指針。
3.我們新建的類,如果需要支持反射機制,那么這個類需要自動將自己的new方法和名字注冊到工廠的映射中。

OK,如果我們能完成以上幾個要求,那么我們在類進行拓展的時候需要改動的地方就十分少了。對於工廠的代碼我們基本上是不會改變的。也就基本上實現了我們C++反射機制的基本功能。

下面我們來一步一步解析代碼:
首先我們還是需要一個Object作為需要支持反射機制類的基類
//Reflex.h
class Object
{
public:
    Object(){}
    virtual ~Object(){}
    static bool Register(ClassInfo* ci);          //注冊傳入一個classInfo(類信息),將這個類的信息注冊到映射中
    static Object* CreateObject(string name);     //工廠生產對象的接口
};
然后是實現:
//Reflex.cpp
static std::map< string, ClassInfo*> *classInfoMap = NULL;
bool Object::Register(ClassInfo* ci)
{
    if (!classInfoMap)   {
        classInfoMap = new std::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
        }
    }
    return true;
}
Object* Object::CreateObject(std::string name)
{
    std::map< string, ClassInfo*>::const_iterator iter = classInfoMap->find(name);
    if (classInfoMap->end() != iter)  {
        return iter->second->CreateObject();         //當傳入字符串name后,通過name找到info,然后調用對應的CreatObject()即可
    }
    return NULL;
}

剩下的我們還需要一個classinfo類就大功告成了:
//Reflex.h
 
typedef Object* (*ObjectConstructorFn)(void);
class ClassInfo
{
public:
    ClassInfo(const std::string className, ObjectConstructorFn ctor)
        :m_className(className), m_objectConstructor(ctor)
    {
        Object::Register(this);             //classInfo的構造函數是傳入類名和類對應的new函數然后自動注冊進map中。
    }
    virtual ~ClassInfo(){}
    Object* CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : 0; }
    bool IsDynamic()const { return NULL != m_objectConstructor; }
    const std::string GetClassName()const { return m_className; }
    ObjectConstructorFn GetConstructor()const{ return m_objectConstructor; }
public:
    string m_className;
    ObjectConstructorFn m_objectConstructor;
};

有了這些類后,我們只需要讓需要支持反射的類滿足以下要求即可:
1.繼承Object類。
2.重載一個CreatObject()函數,里面 return  new 自身類。
3.擁有一個classInfo的成員並且用類名和CreatObject初始化。
滿足以上三個要求的類我們就可以利用反射機制來創建對象了。我們可以看下面的例子:
class B : public Object
{
public:
    B(){ cout << hex << (long)this << " B constructor!" << endl; }
    ~B(){ cout << hex << (long)this << " B destructor!" << endl; }
    virtual ClassInfo* GetClassInfo() const{ return &ms_classinfo; }
    static Object* CreateObject() { return new B; }
protected:
    static ClassInfo ms_classinfo;
};
ClassInfo B::ms_classinfo("B", B::CreateObject);
使用的話我們就只需要調用Object::CreatObject(string) 傳入類名即可。
int main()
{
    Object* obj = Object::CreateObject("B");
    delete obj;
    return 0;
}
基本上反射機制的功能就實現了,而且使用回調注冊在后期拓展上也容易維護。

三.使用宏簡化代碼:

其實大家發現,因為我們要讓類支持反射那么就要滿足我們上面的那三個要求,但是每個類都要寫這樣相似的東西。仔細一看,包括函數申da's明、函數定義、函數注冊,每個類的代碼除了類名外其它都是一模一樣的,有沒有簡單的方法呢? 
那就是使用宏。
//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就可以這樣寫:
class B : public Object
{
    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++反射機制的實現就大功告成了!。

 

#include <iostream>
#include <map>
using namespace std;
//Reflex.h
class ClassInfo;
class Object
{
public:
    Object() {}
    virtual ~Object() {}
    static bool Register(ClassInfo* ci);          //注冊傳入一個classInfo(類信息),將這個類的信息注冊到映射中
    static Object* CreateObject(string name);     //工廠生產對象的接口
};
typedef Object* (*ObjectConstructorFn)(void);

class ClassInfo
{
public:
    ClassInfo(const std::string className, ObjectConstructorFn ctor)
        :m_className(className), m_objectConstructor(ctor)
    {
        Object::Register(this);             //classInfo的構造函數是傳入類名和類對應的new函數然后自動注冊進map中。
    }
    virtual ~ClassInfo() {}
    Object* CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : 0; }
    bool IsDynamic()const { return NULL != m_objectConstructor; }
    const std::string GetClassName()const { return m_className; }
    ObjectConstructorFn GetConstructor()const { return m_objectConstructor; }
public:
    string m_className;
    ObjectConstructorFn m_objectConstructor;
};

//然后是實現:
//Reflex.cpp
static std::map< string, ClassInfo*> *classInfoMap = NULL;

bool Object::Register(ClassInfo* ci)
{
    if (!classInfoMap) {
        classInfoMap = new std::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
        }
    }
    return true;
}
Object* Object::CreateObject(std::string name)
{
    std::map< string, ClassInfo*>::const_iterator iter = classInfoMap->find(name);
    if (classInfoMap->end() != iter) {
        return iter->second->CreateObject();         //當傳入字符串name后,通過name找到info,然后調用對應的CreatObject()即可
    }
    return NULL;
}


//有了這些類后,我們只需要讓需要支持反射的類滿足以下要求即可:
//1.繼承Object類。
//2.重載一個CreatObject()函數,里面 return  new 自身類。
//3.擁有一個classInfo的成員並且用類名和CreatObject初始化。
//滿足以上三個要求的類我們就可以利用反射機制來創建對象了。我們可以看下面的例子:
class B : public Object
{
public:
    B() { cout << hex << (long)this << " B constructor!" << endl; }
    ~B() { cout << hex << (long)this << " B destructor!" << endl; }
    virtual ClassInfo* GetClassInfo() const { return &ms_classinfo; }
    static Object* CreateObject() { return new B; }
protected:
    static ClassInfo ms_classinfo;
};
ClassInfo B::ms_classinfo("B", B::CreateObject);
//使用的話我們就只需要調用Object::CreatObject(string) 傳入類名即可。
int main()
{
    Object* obj = Object::CreateObject("B");
    delete obj;
    return 0;
}
//基本上反射機制的功能就實現了, 而且使用回調注冊在后期拓展上也容易維護。
//————————————————
//版權聲明:本文為CSDN博主「YzlCoder」的原創文章,遵循 CC 4.0 BY - SA 版權協議,轉載請附上原文出處鏈接及本聲明。
//原文鏈接:https ://blog.csdn.net/y1196645376/article/details/51455273
完整代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM