NET下的不少技术都是基于反射机制来实现的,反射让.NET平台下的语言变得驾轻就熟。最简单的,好比枚举类型,咱们我能够很容易的得到一个枚举变量的数值以及其名称字符串。git
但是,在C++中,枚举变量本质上和一个整形变量没有区别,咱们很难获取一个枚举变量的名称字符串。框架
其实在C++中,咱们能够经过宏来实现相似反射的机制。函数
接下来,我想总结一下如何在C++中实现一个相似于C#枚举类型的方法。编码
"##"符号的做用是在可变参数的个数为0时,消除参数前面的逗号:spa
#define MY_PRINTF(fs, …) printf(fs, ##__VA_ARGS__).net
咱们这样调用:设计
MY_PRINTF(“Hello, World”);日志
等价于code
printf(“Hello, World”);对象
另外"##"符号还可以去掉括号,可是我如今还不是很明白,为何可以作到这一点:
"#"符号的做用是“字符化”代码:
#define MY_STRINGLIZED_MACRO(str) #str
int helloWorld = 0;
printf(MY_STRINGLIZED_MACRO(helloWorld)); // output: helloWorld
我作了一个简单的用例,最终示例代码以下:
若是你问一个IT人士“C++如何实现相似Java的反射?”,结果会怎样呢?~!@#¥%……&*,估计大部分人都会要稍微思考了一下,或者直接说“C++根本就不支持反射的呀!”。
是的,C++语言自己是不支持反射的,但实际应用中老是会有将对象序列化的需求,总不可能C++不支持,咱们就不用C++了,既然发明C++的大师们没有考虑这个,那咱们只有本身动手了,毛主席说过“本身动手,丰衣足食”!
天生限制
C++语言自己不支持反射机制,但C++对象老是要序列化的,序列化就是存储到磁盘上,将对象变成必定格式的二进制编码,而后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程当中老是须要有一个指示来告诉编译器要生成什么样的对象,最简单的方式固然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。
可是问题出现了,C++语言自己不支持反射,也就是说不能经过以下方式生成一个对象:
ClassXXX object = new “ClassXXX”;
工厂方法
固然,这样的方法不行,那咱们只有另辟蹊径。最简单的就是工厂方法了:
ClassXXX* object = FactoryCreate(“ClassXXX”);
至于FactoryCreate的设计就很简单了,if的集合就能够了:
if(name = “ClassXXX”)
return new ClassXXX;
if(name = “ClassYYY”)
return new ClassYYY;
看起来不错,来个类名就能够生成对应的对象,功能上解决了根据类名生成对象的问题。
假如以上全部的代码都有你一我的编写,那固然问题不大,可是假若有一天你的公司扩大了,这部分代码由两个不一样的组A和B来维护,啊哈,问题来了,A组每添加或者修改一个类,都要通知B组更新FactoryCreate函数,也就是说A组的任何关于类的修改,都须要B组来修改,但实际上B的修改不产生任何价值,并且不胜其烦,永无止尽!!若是哪天来了一个新员工,因为对这个规定还不清楚,忘记了通知,那就完了:编译通不过!
一个公司内都会产生如此多的问题,更况且微软这样的大公司是面对全球的各类各样的客户,若是微软把这部分作进框架代码中,呵呵,那微软全部的人不用干其余事情了,天天处理来自全球的要求修改FactoryCreate函数的邮件和电话就够他们忙的了:)
回调工厂
既然此路很差走,那么咱们再考虑其它方法吧,一个可选的方法是将FactoryCreate作成回调函数,框架提供注册接口RegisterFactoryCreate,框架函数如此实现:
typedef CObject* (*FactoryCreate_PTR)(String name);
RegisterFactoryCreate(FactoryCreate_PTR fc_ptr);
应用代码如此实现:
CObject* MyFactoryCreate(String name);
RegisterFactoryCreate(MyFactoryCreate);
到这里一个框架和应用分离的反射机制基本实现了,你是否长吁一口气,而后准备泡杯咖啡,稍微放松一下呢?确实能够稍微休息一下了,毕竟咱们完成了一件很是了不得的事情,让C++实现了反射。
但你只清闲了一两天,麻烦事就来了。员工张三跑来向你抱怨“老大,李四注册的反射函数把个人覆盖了”!哦,你仔细一看,My god,这个注册函数只能注册一个反射函数,后注册的就把前面的覆盖了!
怎么办?总不可能又要求全部的类的反射函数都在一个工厂里实现吧,那这样就又回到了工厂方法中描述的时代了。
固然,聪明的你估计很快就能想出问题的解决方法,将RegisterFactoryCreate函数稍加修改就能知足要求了,新的实现以下:
RegisterFactoryCreate(FactoryCreate_PTR fc_ptr,String className)
而后要求每一个类都单独写本身的FactoryCreate_PTR函数,相似以下方式:
static CObject* ClassXXX::CreateClassXXX (){
return new ClassXXX;
};
static CObject* ClassYYY::CreateClassYYY(){
return new ClassYYY;
};
到此为此终于大功告成,经过咱们的智慧实现了C++的反射功能!一股自豪感油然升起:)
最后的杀手锏:宏
当你为本身的聪明才智而骄傲的时候,那边却有几个开发的兄弟在发出抱怨“唉,这么多类似的函数,看着都眼花,每一个类都要写,烦死了”。
或者有一天,你要在每一个类的CreateClass函数中增长一个其它功能(例如日志),那么开发的兄弟真的是要烦“死了”!!!
其实仔细一看,包括函数申明、函数定义、函数注册,每一个类的代码除了类名外其它都是如出一辙的,有没有简单的方法呢?
确定是有的,这个方法就是宏了,按照以下方法定义宏:
注:##是链接符,将两个字符串链接起来,#是将class_name做为字符串处理。
你们能够比较一下,用了宏和不用宏是否是代码感受彻底不同呢?并且那天须要增长一个简单的功能,只须要改宏定义就ok了,不要全文搜索全部相关函数,而后一个一个的重复添加。
到这里才真正是大功告成!!
后记
某天分析Spring的IOC时,看到Digester最后利用的其实是Java的反射机制来根据XML文件定义生成Java对象,突发奇想:若是是C++该怎么办?
因而本身就开始分析起来,分析了一段时间忽然想起微软的MFC不正是要支持C++对象序列化的吗?
赶忙打开深刻浅出MFC,从新将这部分研究了一下。看到微软的天才们在MFC中用宏来实现RTTI、Dynamic Create、Seralize功能时,我反过来思考“若是是我,我会如何设计?”、“为何会这么设计”?而后一一分析这些各类可能的实现方式,一步一步的推导,最后发现居然天然而然的就推出了MFC的这种实现方式!
固然,MFC的实现代码和我给出的代码不同(注册方式不同),但设计思想是同样的,各位看官能够自行稍加分析就明白了。
MFC的详细实现能够参考侯捷的《深刻浅出MFC》。
转载自:http://blog.csdn.net/wangweitingaabbcc/article/details/7916963