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函数中增加一个其它功能(例如日志),那么开发的兄弟真的是要烦“死了”!!!
其实仔细一看,包括函数申明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?
肯定是有的,这个方法就是宏了,按照如下方法定义宏:
#define DECLARE_CLASS_CREATE(class_name) \
static CObject* CreateClass## class_name ();
#define IMPL_CLASS_CREATE(class_name) \
static CObject* CreateClass## class_name (){ \
return new class_name; \
};
#define REG_CLASS_CREATE(class_name) \
RegisterFactoryCreate(class_name::CreateClass## class_name, #class_name);
注:##是连接符,将两个字符串连接起来,#是将class_name作为字符串处理。
大家可以比较一下,用了宏和不用宏是不是代码感觉完全不一样呢?而且那天需要增加一个简单的功能,只需要改宏定义就ok了,不要全文搜索所有相关函数,然后一个一个的重复添加。
到这里才真正是大功告成!!
后记
某天分析Spring的IOC时,看到Digester最后利用的实际上是Java的反射机制来根据XML文件定义生成Java对象,突发奇想:如果是C++该怎么办?
于是自己就开始分析起来,分析了一段时间突然想起微软的MFC不正是要支持C++对象序列化的吗?
赶紧打开深入浅出MFC,重新将这部分研究了一下。看到微软的天才们在MFC中用宏来实现RTTI、Dynamic Create、Seralize功能时,我反过来思考“如果是我,我会如何设计?”、“为什么会这么设计”?然后一一分析这些各种可能的实现方式,一步一步的推导,最后发现竟然自然而然的就推出了MFC的这种实现方式!
C++的反射的更多相关文章
- 隐私泄露杀手锏 —— Flash 权限反射
[简版:http://weibo.com/p/1001603881940380956046] 前言 一直以为该风险早已被重视,但最近无意中发现,仍有不少网站存在该缺陷,其中不乏一些常用的邮箱.社交网站 ...
- Java学习之反射机制及应用场景
前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...
- 关于 CSS 反射倒影的研究思考
原文地址:https://css-tricks.com/state-css-reflections 译者:nzbin 友情提示:由于演示 demo 的兼容性,推荐火狐浏览.该文章篇幅较长,内容庞杂,有 ...
- 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)
建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...
- 运用Mono.Cecil 反射读取.NET程序集元数据
CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改.CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修 ...
- .NET面试题系列[6] - 反射
反射 - 定义,实例与优化 在面试中,通常会考察反射的定义(操作元数据),可以用反射做什么(获得程序集及其各个部件),反射有什么使用场景(ORM,序列化,反序列化,值类型比较等).如果答得好,还可能会 ...
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- C++的性能C#的产能?! - .Net Native 系列五:.Net Native与反射
此系列系小九的学堂原创翻译,翻译自微软官方开发向导,一共分为六个主题.本文是第五个主题:.Net Native与反射. 向导文链接:<C++的性能C#的产能?! - .Net Native 系列 ...
- [源码]Literacy 快速反射读写对象属性,字段
Literacy 说明 Literacy使用IL指令生成方法委托,性能方面,在调用次数达到一定量的时候比反射高很多 当然,用IL指令生成一个方法也是有时间消耗的,所以在只使用一次或少数几次的情况,不但 ...
- SI与EMI(一) - 反射是怎样影响EMI
Mark为期两天的EMC培训中大概分成四个时间差不多的部分,简单来说分别是SI.PI.回流.屏蔽.而在信号完整性的书籍中,也会把信号完整性分为:1.信号自身传输的问题(反射,损耗):2.信号与信号之间 ...
随机推荐
- MySQL索引的学习
MySQL索引的学习 关于使用mysql索引的好处,合理的设计并使用mysql索引能够有效地提高查询效率.对于没有索引的表,单表查询可能几十万数据就是平静,在大型网站单日可能会产生几十万甚至几百万的数 ...
- suse 11.4添加阿里源
感谢SilenMark 作者,让我找到了一个可用的suse 国内源. 大家直接使用root 用户执行以下命令,添加阿里源 zypper addrepo -f http://mirrors.aliyun ...
- Android的文件读取与存储
Java新建文件,然后就可以写入数据了,但是Android却不一样,因为Android是 基于Linux的,我们在读写文件的时候,还需加上文件的操作模式 Environment类是一个提供访问环境变量 ...
- hard(2018.10.18)
题意:给你一棵\(n\)个节点的树,\(q\)个询问,每次询问读入\(u,v,k,op\),需要满足树上有\(k\)对点的简单路径交都等于\(u,v\)之间的简单路径,\(op=1\)表示\(k\)对 ...
- 推荐 VS2010入门教程—鸡啄米
http://www.jizhuomi.com/catalog.asp?tags=VS2010 推荐 VS2010入门教程—鸡啄米,真的非常使用和经典!
- android okhttp和webview session共享
public static OkHttpClient get(Context context){ OkHttpClient.Builder builder = new OkHttpClient.Bui ...
- Servlet | 访问不同格式文件(PDF、doc)
核心代码 //设置响应内容类型为PDF类型 response.setContentType("application/pdf"); request.getRequestDispat ...
- hashlib 实现加密
实现代码 import hashlib # hashlib是一个python用于给数据加密的包,内有很多加密方式,包括md5,sha1, sha224, sha256, sha384, sha512等 ...
- 可视化-grafana_使用influxDB数据
1 添加数据源 给数据源取个名字,然后选择数据类型为influxDB. HTTP:8086是influxDB的HTTP查询API,grafana是通过这个接口获取数据. Details:选择从infl ...
- hdu6314( 2018 Multi-University Training Contest 2)
bryce1010模板 http://acm.hdu.edu.cn/showproblem.php?pid=6314 ----. 又是一个数学题! 这个题使用容斥原理解决的,现场看dls推公式. 我也 ...