1. 什么是反射
學過 java 或 C# 的同學應該都知道“反射”機制,很多有名的框架都用到了反射這種特性。這是一種很牛逼的特性,簡單的理解就是只根據類的名字就可以獲取到該類的實例。有人會說,這不是多此一舉嗎?直接 new 一個出來不就行了嗎?像下面這樣:
class Person {public: virtual void show() = 0;}class Allen : public Person { virtual void show() { std::cout << "Hello, I'm Allen!" << std::endl; }}Person *p = new Allen();p->show();可是有時候,你定義好接口 Person 后,你并不知道誰將會實現該接口,甚至不知道什么時候會實現它。所以此時你無法通過 new 操作符來實例化對象。比如未來的某個時候有人編寫了一個類叫 Luffy,但是此時你不可能實例化 Luffy 這個類,所以你只能編寫下面這種代碼:
std::string className = /*從配置文件中讀取*/Person *p = getNewInstance<Person>(className);你的程序可以從配置文件中讀取到 "Luffy"
這個字符串保存到變量 className
中。接下來使用函數 getNewInstance
就可以獲取到 Luffy 實例化的對象。
2. C++ 實現反射
很遺憾的是 C++ 并沒有直接從語言上提供這種特性給我們用,不過無所不能的 C++ 可以通過一些 trick 來實現反射這種機制。
2.1 引例
直觀上,我們可以把 getNewInstance 的實現交給未來要使用我們的框架的人:
template<typename T>T* getNewInstance(const std::string& className) { if (className == "Allen") { return new Allen(); } else if (className == "Luffy") { return new Luffy(); }}如此一來,一旦有新的類實現,我們就必須得修改這個函數,這很容易出錯,維護性很差。
2.2 對象工廠
對象工廠是一種可以間接實例化對象的類。比如:
class ObjectFactory{ virtual ReflectObject* newInstance() = 0;}class ObjectFactory_Allen : public ObjectFactory{ ReflectObject* newInstance(){ // 這里注意一點就是,所有能夠被反射的類都繼承自 ReflectObject 這個類。 return new Allen(); };}所以,如果有了對象工廠的實例,我們就可以不斷的產生對象了。如下:
ObjectFactory *of = new ObjectFactory_Allen();// 有了對象工廠的實例后,就可以產生 Allen 對象的實例了ReflectObject *allen= of->newInstance();// 接下來可以使用類型轉換,把 allen 對象轉換成 PersonPerson *p = dynamic_cast<Person*>(allen);2.3 反射器
反射器實際上也是一個類,它管理了類名到對象工廠實例之間的映射關系。反射器對象在程序中是一個全局唯一的對象。這種映射關系看起來就像一張表:
("Allen", Allen 的對象工廠的實例對象);("Luffy", Luffy 的對象工廠的實例對象);("Zoro", Zoro 的對象工廠的實例對象);……只要未來你想要編寫一個新的類,比如 Allen 類,那么你就必須要同時編寫 Allen 的工廠類 ObjectFactory_Allen,同時,你還得實例化一個這個工廠類的對象 objectFactory_Allen = new ObjectFactory_Allen(),并將 ("Allen", objectFactory_Allen)
這種映射關系保存到反射器中。
反射器的定義如下:
class Reflector{public: Reflector(); ~Reflector(); // 如果你要反射你的類,就必須將你的類名,以及工廠實例對象注冊到反射器中。 void registerFactory(const std::string& className, ObjectFactory *of); // 反射器可以根據對象工廠實例來生產實例對象。請參考 2.2 節 ReflectObject* getNewInstance(const std::string& className);PRivate: std::map<std::string, ObjectFactory*> objectFactories;};2.4 編寫要被反射類的大致思路
有了上面的基礎好,要想讓你的類被反射,大致有以下幾個步驟:
讓你的類繼承某個接口(該接口繼承自 ReflectObject 類)編寫一個對應的工廠類,并實例化一個工廠類對象調用反射器的 registerFactory 接口,將你的類名和工廠類對象保存到反射器中特別的,上述第 2 和 3 步很明顯是屬于成年不變的步驟,它可以使用宏函數來實現。在你完成步驟 1 后,就可以使用宏函數一次完成。在這里,將步驟2 和 3 統一稱為“注冊反射類”。
2.5 注冊反射類
注冊反射類實際上就是 2.4 中的第 2 和 3 步,其代碼如下:
步驟 1:定義 Allen 類(略)步驟 2:定義 Allen 的工廠類class ObjectFactory_Allen : public ObjectFactory{ ReflectObject* newInstance(){ return new Allen(); };}步驟 3:創建 Allen 工廠類實例對象并注冊到反射器中// 函數 reflector 可以用來獲取反射器對象。reflector().registerFactory("Allen", new ObjectFactory_Allen()); 為了能讓上面的代碼復用,使用宏函數來進行改寫:
/***********需要被反射的類,需要在其對應的 cpp 文件中進行反射聲明***********/#define REFLECT(name)/class ObjectFactory_##name : public ObjectFactory{/public:/ ReflectObject* newInstance() {/ return new name(); / }/}; /class Register_##name{/public:/ Register_##name(){/ reflector().registerFactory(#name, new ObjectFactory_##name()); / }/};/Register_##name register_##name;這樣,以后步驟 2 和 步驟 3 就可以簡寫成:
REFLECT(Allen);看起來是不是很方便?
上面的宏函數并沒有直接調用反射器的注冊函數,而是先定義了一個注冊類對象,在注冊類對象的構造函數中完成了調用,然后又定義了一個該類的全局對象,以達到自動調用構造函數的目的。想一想,為什么不直接調用反射器的注冊函數?
3. 完整示例
3.1 客戶端部分
main 函數#include "Person.h"#include "Reflector.h"int _tmain(int argc, _TCHAR* argv[]){ Person *allen = getNewInstance<Person>("Allen"); Person *luffy = getNewInstance<Person>("Luffy"); allen->show(); luffy->show(); delete allen; delete luffy; return 0;}用戶編寫的 Allen 類和 Luffy 類#include "Person.h"class Allen : public Person{public: Allen() virtual ~Allen(); virtual void show();};class Luffy : public Person{public: Luffy(); virtual ~Luffy(); virtual void show();};//兩個類的實現,在 cpp 文件中REFLECT(Allen);// 注冊反射類,只能寫在 cpp 文件中。Allen::Allen(){ std::cout << "Allen()" << std::endl;}Allen::~Allen(){ std::cout << "~Allen()" << std::endl;}void Allen::show(){ std::cout << "Hello, I'm Allen" << std::endl;}REFLECT(Luffy); // 注冊反射類,只能寫在 cpp 文件中。Luffy::Luffy(){ std::cout << "Luffy()" << std::endl;}Luffy::~Luffy(){ std::cout << "~Luffy()" << std::endl;}void Luffy::show(){ std::cout << "Hello, I'm Luffy" << std::endl;}3.2 框架部分
Person 接口定義與實現// Person.h#include "Reflector.h"http:// 讓 Person 繼承反射基類class Person : public ReflectObject{public: Person(); virtual ~Person(); virtual void show();};// Person.cppPerson::Person(){ std::cout << "Person()" << std::endl;}Person::~Person(){ std::cout << "~Person()" << std::endl;}void Person::show(){ std::cout << "Hello, I'm person" << std::endl;}反射器部分// Reflect.h#pragma once#include <string>#include <map>#include <iostream>/********************所有需要實現反射的類需要繼承它************************/class ReflectObject { public: virtual ~ReflectObject(){}};/************************************************************************//******************對象工廠抽象類,用來生成對象實例************************/class ObjectFactory {public: ObjectFactory(){ std::cout << "ObjectFactory()" << std::endl; } virtual ~ObjectFactory(){ std::cout << "~ObjectFactory()" << std::endl; } virtual ReflectObject* newInstance() = 0;};/************************************************************************//***********反射器,用來管理(對象名,對象工廠)的映射關系******************/class Reflector{public: Reflector(); ~Reflector(); void registerFactory(const std::string& className, ObjectFactory *of); ReflectObject* getNewInstance(const std::string& className);private: std::map<std::string, ObjectFactory*> objectFactories;};/************************************************************************//**********************獲取反射器實例,全局唯一****************************/Reflector& reflector();/************************************************************************//***********需要被反射的類,需要在其對應的 cpp 文件中進行反射聲明***********/#define REFLECT(name)/class ObjectFactory_##name : public ObjectFactory{/public:/ ObjectFactory_##name(){ std::cout << "ObjectFactory_" << #name << "()" << std::endl; }/ virtual ~ObjectFactory_##name(){ std::cout << "~ObjectFactory_" << #name << "()" << std::endl; }/ ReflectObject* newInstance() {/ return new name(); / }/}; /class Register_##name{/public:/ Register_##name(){/ reflector().registerFactory(#name, new ObjectFactory_##name()); / }/};/Register_##name register_##name;/************************************************************************//***********************根據類名獲取對象實例******************************/template<typename T>T* getNewInstance(const std::string& className) { return dynamic_cast<T*>(reflector().getNewInstance(className));}/************************************************************************/// Reflector.cpp#include "Reflector.h"Reflector::Reflector(){}Reflector::~Reflector(){ std::map<std::string, ObjectFactory*>::iterator it = objectFactories.begin(); for (; it != objectFactories.end();++it) { delete it->second; } objectFactories.clear();}void Reflector::registerFactory(const std::string& className, ObjectFactory *of){ std::map<std::string, ObjectFactory*>::iterator it = objectFactories.find(className); if (it != objectFactories.end()) { std::cout << "該類已經存在……" << std::endl; } else { objectFactories[className] = of; }}ReflectObject* Reflector::getNewInstance(const std::string& className){ std::map<std::string, ObjectFactory*>::iterator it = objectFactories.find(className); if (it != objectFactories.end()) { ObjectFactory *of = it->second; return of->newInstance(); } return NULL;}// 用來獲取反射器對象,注意這是全局唯一的。Reflector& reflector() { static Reflector reflector; return reflector;}3.3 運行結果
4. 總結
反射機制的實現,主要在于工廠模式的靈活使用。另外,還需要掌握一些 C++ 代碼技巧,比如如何在 main 函數之前進行初始化操作,反射器的初始化以及生產工廠類對象很好的體現了全局對象的應用。
理解多態,接口,繼承掌握對象工廠的使用方法掌握工廠模式理解反射器的工作原理掌握宏函數的編寫代碼下載:http://git.oschina.net/ivan_allen/Reflection