C++ 实现反射机制
来源:互联网 发布:html5编程软件 编辑:程序博客网 时间:2024/06/05 15:29
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"// 让 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
- c 结构体反射机制实现
- 【Object C】 反射机制
- Objective-C反射机制
- 【C#】:浅谈反射机制
- Object-c反射机制
- 【C#】反射机制
- java利用反射实现Object-c中的KVC机制
- 反射机制的实现
- JavaScript 实现反射机制
- iOS实现反射机制
- iOS实现反射机制
- C++实现反射机制
- C++实现反射机制
- C++实现反射机制
- c++实现反射机制
- C++ 实现反射机制
- C++实现反射机制
- C++ 实现反射机制
- 程序中的小细节——datepicker的两种显示方式,spinner和calendar
- linux 安装hadoop是32位的需要手工编译成64位
- 安装Halcon10.0
- Java HashMap分析及其它
- React Native网络请求学习笔记(Android版本)
- C++ 实现反射机制
- 不同形状的头像
- js学习笔记:严格模式
- BZOJ 3672 [Noi2014]购票【点分+斜率优化
- 在Fragment中webView的回退问题
- 系统开发奥若拉模式
- 约瑟夫环
- REDIS 进阶(12) redis分片
- Caffe使用教程【转】