C++ 反射機(jī)制詳解及實(shí)例代碼
C++ 反射機(jī)制
一.前言:
Java有著一個(gè)非常突出的動(dòng)態(tài)相關(guān)機(jī)制:Reflection,用在Java身上指的是我們可以于運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes。換句話(huà)說(shuō),Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱(chēng)的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對(duì)象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。然而C++是不支持反射機(jī)制,雖然C++有RTTI(運(yùn)行時(shí)類(lèi)型識(shí)別)。但是想要實(shí)現(xiàn)C++對(duì)象序列化,序列化就是存儲(chǔ)到磁盤(pán)上,將對(duì)象變成一定格式的二進(jìn)制編碼,然后要用的時(shí)候再將保存在磁盤(pán)上的二進(jìn)制編碼轉(zhuǎn)化成一個(gè)內(nèi)存中的對(duì)象,這個(gè)過(guò)程中總是需要有一個(gè)指示來(lái)告訴編譯器要生成什么樣的對(duì)象,最簡(jiǎn)單的方式當(dāng)然就是類(lèi)名了,例如:將一個(gè)ClassXXX對(duì)象存儲(chǔ)到磁盤(pán)上,再?gòu)拇疟P(pán)讀取的時(shí)候讓編譯器根據(jù)“ClassXXX”名稱(chēng)來(lái)new一個(gè)對(duì)象。
ClassT* obj = FactoryCreate("ClassT");
類(lèi)似于以上的語(yǔ)法,雖然C++沒(méi)有自帶的語(yǔ)法可以實(shí)現(xiàn),但是我們可以自己通過(guò)其他方法來(lái)實(shí)現(xiàn)。(由于本人能力有限,所以該篇博客只是講解如何簡(jiǎn)單的實(shí)現(xiàn)這個(gè)反射機(jī)制,而對(duì)C++中擁有這個(gè)反射機(jī)制是否有必要不做任何討論。當(dāng)然,如果博客中有什么地方說(shuō)的有錯(cuò)誤還望大家可以在下面評(píng)論指出謝謝)
二.實(shí)現(xiàn):
1.我們很容易可以想到可以使用簡(jiǎn)單工廠(chǎng)模式來(lái)實(shí)現(xiàn)這個(gè)效果:比如
class Object
{
public:
virtual string ToString() = 0;
};
這個(gè)是所有需要實(shí)現(xiàn)反射機(jī)制的類(lèi)需要繼承的基類(lèi),然后派生出來(lái)的類(lèi)只需要再實(shí)現(xiàn)這個(gè)ToString即可。例如:
class MyClass :public Object
{
public:
virtual string ToString(){ return "MyClass"; }
};
然后就是用于產(chǎn)生對(duì)象的工廠(chǎng)。
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;
}
我們使用簡(jiǎn)單工廠(chǎng)模式感覺(jué)好像是解決了問(wèn)題,可以實(shí)現(xiàn)用字符串去new一個(gè)對(duì)應(yīng)的對(duì)象,但是假如我們要新建一個(gè)類(lèi)或者修改一個(gè)類(lèi),那么這個(gè)FactoryCreat都要進(jìn)行修改。十分不利于維護(hù)。所以我們需要換一個(gè)方式來(lái)處理。
2.工廠(chǎng)模式結(jié)合回調(diào)機(jī)制。
首先我們要梳理一下這個(gè)方法的基本脈絡(luò):
1.工廠(chǎng)內(nèi)部需要有個(gè)映射,也就是一個(gè)字符串對(duì)應(yīng)一個(gè)類(lèi)new的方法。
2.工廠(chǎng)給出一個(gè)接口,我們傳入字符串,那么返回這個(gè)字符串對(duì)應(yīng)的方法new出來(lái)的對(duì)象指針。
3.我們新建的類(lèi),如果需要支持反射機(jī)制,那么這個(gè)類(lèi)需要自動(dòng)將自己的new方法和名字注冊(cè)到工廠(chǎng)的映射中。
OK,如果我們能完成以上幾個(gè)要求,那么我們?cè)陬?lèi)進(jìn)行拓展的時(shí)候需要改動(dòng)的地方就十分少了。對(duì)于工廠(chǎng)的代碼我們基本上是不會(huì)改變的。也就基本上實(shí)現(xiàn)了我們C++反射機(jī)制的基本功能。
下面我們來(lái)一步一步解析代碼:
首先我們還是需要一個(gè)Object作為需要支持反射機(jī)制類(lèi)的基類(lèi)
//Reflex.h
class Object
{
public:
Object(){}
virtual ~Object(){}
static bool Register(ClassInfo* ci); //注冊(cè)傳入一個(gè)classInfo(類(lèi)信息),將這個(gè)類(lèi)的信息注冊(cè)到映射中
static Object* CreateObject(string name); //工廠(chǎng)生產(chǎn)對(duì)象的接口
};
然后是實(shí)現(xiàn):
//Reflex.cpp
static std::map< string, ClassInfo*> *classInfoMap = NULL;
bool Object::Register(ClassInfo* ci)
{
if (!classInfoMap) {
classInfoMap = new std::map< string, ClassInfo*>(); //這里我們是通過(guò)map來(lái)存儲(chǔ)這個(gè)映射的。
}
if (ci) {
if (classInfoMap->find(ci->m_className) == classInfoMap->end()){
classInfoMap->insert(std::map< string, ClassInfo*>::value_type(ci->m_className, ci)); // 類(lèi)名 <-> 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(); //當(dāng)傳入字符串name后,通過(guò)name找到info,然后調(diào)用對(duì)應(yīng)的CreatObject()即可
}
return NULL;
}
剩下的我們還需要一個(gè)classinfo類(lèi)就大功告成了:
//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的構(gòu)造函數(shù)是傳入類(lèi)名和類(lèi)對(duì)應(yīng)的new函數(shù)然后自動(dòng)注冊(cè)進(jìn)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;
};
有了這些類(lèi)后,我們只需要讓需要支持反射的類(lèi)滿(mǎn)足以下要求即可:
1.繼承Object類(lèi)。
2.重載一個(gè)CreatObject()函數(shù),里面 return new 自身類(lèi)。
3.擁有一個(gè)classInfo的成員并且用類(lèi)名和CreatObject初始化。
滿(mǎn)足以上三個(gè)要求的類(lèi)我們就可以利用反射機(jī)制來(lái)創(chuàng)建對(duì)象了。我們可以看下面的例子:
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);
使用的話(huà)我們就只需要調(diào)用Object::CreatObject(string) 傳入類(lèi)名即可。
int main()
{
Object* obj = Object::CreateObject("B");
delete obj;
return 0;
}
基本上反射機(jī)制的功能就實(shí)現(xiàn)了,而且使用回調(diào)注冊(cè)在后期拓展上也容易維護(hù)。
三.使用宏簡(jiǎn)化代碼:
其實(shí)大家發(fā)現(xiàn),因?yàn)槲覀円岊?lèi)支持反射那么就要滿(mǎn)足我們上面的那三個(gè)要求,但是每個(gè)類(lèi)都要寫(xiě)這樣相似的東西。仔細(xì)一看,包括函數(shù)申da's明、函數(shù)定義、函數(shù)注冊(cè),每個(gè)類(lèi)的代碼除了類(lèi)名外其它都是一模一樣的,有沒(méi)有簡(jiǎn)單的方法呢?
那就是使用宏。
//Reflex.h
//類(lèi)申明中添加 classInfo 屬性 和 CreatObject、GetClassInfo 方法
#define DECLARE_CLASS(name) \
protected: \
static ClassInfo ms_classinfo; \
public: \
virtual ClassInfo* GetClassInfo() const; \
static Object* CreateObject();
//實(shí)現(xiàn)CreatObject 和 GetClassInfo 的兩個(gè)方法
#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;}
有了宏替換后,我們定義一個(gè)新的類(lèi)。
只需要在類(lèi)定義中添加 DECLARE_CLASS(classname) 實(shí)現(xiàn)中添加IMPLEMENT_CLASS(classname)就可以讓這個(gè)類(lèi)實(shí)現(xiàn)反射了。
例如我們上面的類(lèi)B就可以這樣寫(xiě):
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)
這樣不管以后需要添加、修改什么功能都只需要修改宏就可以了而不需要每個(gè)類(lèi)每個(gè)類(lèi)去添加、修改方法。
ok到這里基本上,c++反射機(jī)制的實(shí)現(xiàn)就大功告成了!。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
C++實(shí)現(xiàn)掃雷游戲(控制臺(tái)不閃屏版)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)掃雷游戲,控制臺(tái)不閃屏版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
C語(yǔ)言順序表的基本結(jié)構(gòu)與實(shí)現(xiàn)思路詳解
順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線(xiàn)性結(jié)構(gòu),一般情況下采用數(shù)組存儲(chǔ)。本文將通過(guò)示例為大家講解一下順序表的基本操作,需要的可以參考一下2023-02-02
C/C++實(shí)現(xiàn)快速排序算法的思路及原理解析
這篇文章主要介紹了C/C++實(shí)現(xiàn)快速排序算法的思路及原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
C++ LeeCode題目:比特位計(jì)數(shù)和買(mǎi)賣(mài)股票的最佳時(shí)機(jī)
這篇文章主要介紹了基于C語(yǔ)言計(jì)算比特位計(jì)數(shù)和買(mǎi)賣(mài)股票的最佳時(shí)機(jī),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-07-07
wince程序防止創(chuàng)建多個(gè)實(shí)例實(shí)現(xiàn)互斥作用
什么時(shí)候用的互斥?當(dāng)你的程序只允許同時(shí)打開(kāi)一個(gè)的時(shí)候,就可以通過(guò)互斥來(lái)實(shí)現(xiàn),下面說(shuō)的互斥,主要是針對(duì)防止程序創(chuàng)建多個(gè)實(shí)例這種情況來(lái)實(shí)現(xiàn)的2014-02-02
C++11 thread多線(xiàn)程編程創(chuàng)建方式
這篇文章主要介紹了C++11 thread多線(xiàn)程編程的相關(guān)知識(shí),包括線(xiàn)程的創(chuàng)建方式結(jié)束方式及互斥鎖的實(shí)例代碼詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
C語(yǔ)言實(shí)現(xiàn)串的順序存儲(chǔ)表示與基本操作
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)串的順序存儲(chǔ)表示與基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
C++?LeetCode1769移動(dòng)所有球到每個(gè)盒子最小操作數(shù)示例
這篇文章主要為大家介紹了C++?LeetCode1769移動(dòng)所有球到每個(gè)盒子所需最小操作數(shù)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

