COM本质论 笔记

本文介绍了COM(Component Object Model)的本质,通过从C++静态库到动态链接库(DLL)的演进,探讨了C++在重用性和跨编译器兼容性方面的问题。作者指出,C++缺乏二进制级别的标准导致的名称改编和编译器不兼容性是主要障碍。为解决这些问题,引入了接口类和抽象基类作为二进制接口,确保了封装性和编译器兼容性。最后,讨论了对象扩展性和资源管理,提出了引用计数的概念,以实现对象的自动管理。
摘要由CSDN通过智能技术生成

COM本质论的第一章看了很多遍, 看了忘, 忘了看, 一直觉得写的太好了, 看了有醍醐灌顶的感觉, 最终觉得这么忘下去不是个事, 尤其年纪大了, 还是来记些笔记吧.

这本书属于如果你不是COM的设计者, 没有那种陪COM一路走来的过程, 是绝对写不出来的. 个人认为任何复杂的理论, 只要你了解它的始末, 都可以用比较容易理解的方式描述出来, 这就是科普的可行性. 某些故作高深的专家, 所谓的那些不知其所云的书和文章, 真是让人深深的鄙视...

 

本书的这一章主要是描述了从C++静态库, 一直演化到COM的C++原型的过程, 到这章的结束, 我们看到的仍然是C++, 它是可以跨编译器的, 但它已经具备了所有COM所具备的主要特性.

 

C++对于C而言, 主要就是增加了面向对象特性, 即允许程序员建立用户自定义类型(user-defined type), 类, 而面向对象的主要动机之一就是重用性, 这些类可以在原始实现环境之外被重复使用.

但事实上, 要编写很容易被重用的C++类非常的困难, 除了在设计和开发时的不规范导致的重用障碍, 更重要的时在运行时也有大量的障碍, 使的C++对象模型不能够成为"可重用软件组件"的理想底层基础.

这种运行时的障碍主要来自, C++没有二进制级别的规范, 导致各个编译器厂家对很多比较灵活的语言特性的二进制实现是不兼容的, 所以C++开发的库无法跨编译器调用. 下面就从C++静态库开始一步步的讲.

 

静态库

对于静态库而言, 编译器会把你用到的库函数, 拷贝一份到你的程序中, 然后一起编译.

静态库问题就是,

1. 一旦库开发者更新实现, 你必须重新编译你的程序才能更新库, 这个是比较烦的, 尤其当程序规模比较大的时候.

2. 对于静态库, 如果你有多个程序同时用到该库, 那必须在每个程序中copy一份, 经行编译. 当库比较大时, 对内存是比较浪费的.

静态库在编译完成之后, 就完全融入你的程序, 模块化特征几乎消失.

 

动态链接库 (DLL)

动态链接库可以作为解决静态库问题的一种技术, 当把静态库封装成DLL时, 静态库的所有方法会被加到DLL的引出表(export list)中, 引出表允许在运行时把每个方法的名字解析到内存中对应的地址. 并且链接器还会产生一个引入库(import library), 引入库只是包含了一些引用, 这些引用指向DLL的文件名和被引出的符号名字.

 

所以当你的程序编译时, 只需要链接引入库, 引入库是很小的, 不浪费空间, 里面只是记录了DLL文件的地址和DLL暴露的接口符号. 这样也缩短了编译时间.

当程序运行时, 当调用到DLL中的库函数时, 程序才根据引入库中的DLL文件地址取装载DLL, 并且通过引出接口符号去引出表中查到方法在内存中的地址, 进行调用.

这样的话, 如果有多个程序调用这个库函数, 我们也只需要在内存中保留一份库, 而不用想静态库一样在每个程序中保留一份.

那么动态链接库是从原始的C++类走向可替换的, 有效的可重用组件的重要一步.

但是这只是其中一步, 光这一步还无法完成重用组件, 因为C++有底下两个问题.

 

可移植性问题

C++基本弱点之一是象前面讲的缺乏二进制一级的标准, 就是各个编译器是不兼容的.

最明显的问题是, 为了允许函数重载, 编译器往往会篡改每个函数的函数名, 称为名字改编(name mangling).

要命的是各个编译器厂商的改编规则是不一样的, 这个没有个标准.

所以用不用的编译器编译的程序和DLL库, 可能无法完成调用, 因为你在程序中的改编好的函数名, 到引入库中找不到, 引入库里面的函数名被另一个编译器改编了.

有个解决的方法是加上extern 'c' 阻止名字改编, 具体参见C语言易混淆关键词详解-const, static, extern, typedef, 声明

但这个方法只能用于全局函数, 对于成员函数不可行

另一个方法,是为不同的编译器产生不用的引入库, 这个方法明显很麻烦

 

而且上面只是移植性问题的一个例子, 除了最简单的语言结构外, 所有编译器厂商都会选择自己专有的方式来实现语言的其他特征.这个问题就很崩溃了, 当时标准没指定完善导致重用性的问题很难解决.

 

封装性问题

C++通过类对数据和操作进行封装, 类只暴露public的成员函数作为接口, 其他的成员变量和函数对用户透明.

这样在改动类的内部实现时, 不需要修改客户的代码. 这样是体现了代码的封装性.

对于动态链接库而言, 客户代码和类库是分开编译的, 当类库中的类的实现发生改变, 增加了成员变量, 是否可以只编译类库, 而不编译客户代码, 答案是有可能是会出错的.

原因在于C++的封装性不是二进制级别的, 看如下例子

class __declspec(dllexport) FastString {
    const int m_cch; //新增加的成员变量
    char *m_psz;
public:
    FastString(const char *psz);
    ~FastString(void);
    int Length(void) const;
    int Find(const char *psz) const;
} ;

为类库中的FastString类, 增加了成员变量m_cch, 编译完类库后, 客

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值