注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

COM 本质论 读书笔记 2  

2008-12-29 16:14:06|  分类: COM |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

兼容性问题源于不同的编译器对于以下两个方面的不同考虑方案:

1. 如何在运行时表现语言的特征;

2. 在连接时刻如何表达符号名字;

保证C++接口类所强加的二进制防火墙只使用与编译器无关的语言特征,这可以解决编译器/连接器的依赖性问题。为了实现这种独立性,我们必须首先确定语言的哪些方面具有同一的实现。复合类型(比如struct)旺旺一样。这是第一个假设,有时候利用带条件编译的类型定义编译指示(pragma)或其他编译器指示符(compiler directive)可以做到这一点。第二个假设是,所有编译器都强制使用同样的顺序传递函数参数,并且堆栈的清理也必须按照统一的方式进行。Win32 API的WINAPI/WINBASEAPI宏就是这项技术的一个例子。第三个假设:某个给定平台上的所有C++编译器都实现了同样的虚函数调用机制。这个假设只需要适用于“没有数据成员、并且至多只有一个基类(基类液没有数据成员)”的类即可。虽然所有的编译器产生的机器代码不必完全相同,但是必须等价。这意味着对于每个编译器来说,一个类的对象在内存中如何表示,以及在运行时虚函数如何被动态调用的,都必须遵从同样的假设。

在C++中,虚函数的运行时实现采用了vptr和vtbl的形式,几乎所有的编译器产品都是这样。这项技术的基础是:编译器在后台为每个包含虚函数的类产生一个静态函数指针数组。这个数组也叫虚函数表(vtbl),在这个类或者他的基类中定义了每个虚函数都有相应的函数指针。该类的每个实例包含一个不可见的数据成员—虚函数指针(vptr),这个指针被构造函数自动初始化,指向类的vtbl。当客户调用虚函数的时候,编译器产生代码反指向到vptr,索引到vtbl中,然后再指定的位置上找到函数指针,并发出调用。这就是C++中实现多态性以及动态调用分发发的过程(详细原理和机制可以参见《深度探索C++对象模型》一书)。

即使数据类型的公共操作已经被提升成为抽象基类中的纯虚函数了,如果客户没有实现类的定义得花,它仍然不能构造类的对象。把实现类的定义暴露给客户等于绕过了接口的二进制封装,从而破坏了使用接口的基本意图。为了使客户可以构造对象,一种合理可行的技术是让DLL引出一个全局函数,由他代表客户调用new操作符。这个函数必须以extern “C”的方式引出,因此任何一个 C++编译器都可以访问这个函数。如同句柄类的方法一样,new操作符仅仅在DLL 内部被调用,这意味着对象的大小和布局结构将使用与“编译实现类的所有方法”相同的编译器建立起来。

最后一个有待于进一步克服的障碍与对象的析构函数有关。针对这个问题一个显而易见的解决方案是吧接口类做成虚函数,不幸的是这样将会破坏接口类的编译器独立性,因为虚析构函数在vtbl中的位置随着编译器的不同而不同。对于这个问题,一个可行的解决方案是显式的增加一个Delete方法,作为接口类的另一个纯虚函数,并且让派生类在这个方法实现中删除自身,这样可以导致正确的析构过程 。

用抽象基类作为接口来实现对象,这项技术有几个可能的应用。使用这项技术的一个动机是:当客户程序在没有安装对象实现的机器上运行时,他可以避免操作系统产生错误。另一个动机是,他可以减少进程地址空间初始化的工作。而且因为DLL并没有在进程的初始化的时候被自动装载进来,所以,如果对象实现代码没有被真正使用的话,那么DLL永远也不会被装载进来。其他好处:加快客户启动速度、长时间运行的进程可以保留更多的地址空间。

这项技术最有趣的应用之一可能是,允许客户在同一接口的不同实现直接动态的做出选择。

到现在为止所展示的技术使得客户可以动态的选择并装载二进制组件,从而可以随着时间的推移不断的升级他们的实现,而客户无须重新编译。对于创建动态的复合系统来说,这绝对是非常有用。然而,对象的接口却不能随着时间不断进化。这是因为,客户编译过程中需要有精确的接口类定义,对接口定义的任何变化都要求客户程序重新编译,以便适应这种变化。更糟糕的是,改变接口的定义完全违背了对象的封装性,因为对象的公开接口已经被改变了,而且这样做也会伤害到现有客户程序。这意味着接口必须是不可改变的,一旦公开后,接口就不能在变化了。这个问题的解决办法是“允许实现类暴露多个接口”。这可以通过两种途径获得:设计一个接口使他继承另一个相关接口,或者让实现类继承多个不相关的接口。客户可以使用C++的运行时类型识别(RTTI)功能在运行时询问对象。但是问题是RTTI是一个与编译器机器相关的特征。C++草案工作文档规定了RTTI的语法和语义,但是每个编译器厂商对RTTI的实现则独立的,也是私有的。这就大大破坏了“以抽象基类作为接口而活得的编译器独立性”。对于这个问题,一个简洁的办法是,平衡处理dynamic_cast的语义,不使用实际与编译器相关的语言特征,从每一个接口显式的暴露一个广为人知的方法,由这个方法完成与dynamic_cast语义等价的功能,这样也可以获得同样的效果,而且不要求所有各方都使用同样的C++编译器。

  评论这张
 
阅读(141)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017