COM是一个更好的C++

昨天看了《COM本质论》的第一章”COM是一个更好的C++”,觉得很有必要做一些笔记,于是整理成这篇文章,我相信你值得拥有。

这篇文章主要讲的内容是:一个实现了快速查找功能的类FastString,在一个小小的需求之后,慢慢的演变成一个COM组件的过程。

类FastString实现了一个快速查找字符串的功能,快到时间复杂度是O(1),我们先不管作者是怎么实现的,估计是通过空间换时间。由于这个类查找字符串很快,于是作者就把这个类当做一个产品,以源码的方式卖给需要的厂商,厂商用后感觉很好,但有的厂商想要获得字符串长度的功能,他们觉得strlen(str)速度太慢,毕竟这个函数获取字符串的长度是线性的,时间复杂度是O(N),于是作者决定修改他的FastString,其内心一直在告诉自己:我的FastString必须是Fast。

我们先来看看作者FastString的样子:

class FastString
{
public:
      FastString(const char* str);
      FastString(void );
      int Find (const char* str );
private:
      char* m_str ;
};

可别小看这个类,它查找字符串可快了(我也不知道为什么它就他妈的这么快)。聪明的作者听了厂商的需求之后,很快的就想到了很好的解决方案,通过一个变量len来存字符串的长度,通过一个函数Length返回变量len,时间复杂度可是O(0)哦,于是作者很快的实现了厂商的需求,大概如下:

class FastString
{
public:
      FastString(const char* str);
      FastString(void );
      int Length ();//新增的
      int Find (const char* str );
private:
      char* m_str ;
      int len ;//新增的
};

在经过天衣无缝的测试之后,作者骄傲的将他的作品分发给了愿意再次掏钱的厂商,厂商用了很是火大,出现了各种莫名其妙的问题,在被各个厂商咆哮之后,作者发现了他的作品的缺陷,于是决定走上COM之路

我们先来看看厂商用了作者的FastString之后为什么就挂了呢?

厂商们拿了作者的源码之后,就以源码的方式和自己的其他代码一起编译成一个DLL文件,然后让自己的产品升级,升级就是简单的覆盖这个DLL文件,于是厂商的产品升级之后就挂了。因为FastString可能在多个DLL中多个文件都实例化了,在这些DLL中FastString占用4个字节的内存,而新版本的FastString占用的是8个字节的内存,厂商只覆盖了FastString所在的DLL,而没有覆盖所有使用了FastString的DLL,由于FastString所在的DLL创建FastString是8个字节,而其他DLL中是4个字节,如果跨库传递FastString,将一个4字节的对象当做一个8字节的对象来用,这还不挂。

聪明的作者很快就实现了他的COM组件,源码大概是下面这个样子,不要奇怪为什么作者的COM之路这么顺风顺水,这么快就出了作品。

#pragma once
class IExtensibleObject
{
public:
    virtual void* Dynamic_Cast(const char* str)=0;
    virtual void AddRef()=0;
    virtual void Release()=0;
};

class IFastString:public IExtensibleObject
{
public:
    virtual int Length(void)=0;
    virtual int Find(const char* str)=0;
};

class FastString:public IFastString
{
public:
    FastString(const char* str=NULL);
    virtual void* Dynamic_Cast(const char* str);
    virtual void AddRef() ;
    virtual void Release();
    virtual int Length();
    virtual int Find(const char* str);
    ~FastString();
private:
    char* m_str;
    int len;
    int m_cPtrs;//引用计数
};

//导出函数
extern "C" __declspec(dllimport) IFastString*  CreateFastString(const char* psz);

作者的COM组件做到了一下几点,终于实现了增量更新。

1:作者不在以源码的方式卖给厂商,而是以头文件和库的方式卖个厂商,厂商可以通过静态/动态的方式链接作者的库。

2:作者不在让厂商到处实例化他的FastString,我可爱的FastString。而是通过一个导出函数实例化FastString,并返回IFastString,这样就不会出现不同DLL中FastString实例大小不一样的问题。现在所有的实例都在作者的DLL中创建了。

3:关于回收FastString的问题?作者刚开始是想直接delete掉CreateFastString返回的指针,但为了实现COM组件,此时的FastString已经不是彼时的自己了,他继承并实现了多个接口,由于接口之间转换来转换去,都不知道删除哪个指针了,于是作者决定通过使用引用计数的方式销毁FastString。

4:为什么要自己实现Dynamic_Cast?

RTTI是一个与编译器极为相关的特征,每个编译器厂商对RTTI的实现是独有的,这大大破坏了“以抽象基类作为接口而获得的编译器独立性”,既然每个编译器可能有不同的实现,即析构函数不能定义成虚函数,因为不同的编译器,虚函数在虚方法表中的位置是不一样的,有的编译器放在最前面有的放在最后面,这会导致不同的编译器编译后虚方法在虚方法表中的位置是不一样的。所以析构函数不能定义成virtual,其他public接口都必须定义成virtual。其他虚方法在虚方法表中的位置和虚方法的声明保持一致,即按照声明的顺序存放在虚方法中。

由于类型转换和引用计数是每个接口都需要的,于是把他们提出来放到最顶层,让所有的接口继承它。

5:新增的接口只能加在最后面,废弃的接口不能删除。

如果新增的接口插在中间,那么部分接口在虚方法表中的地址就会发生变化,新版本的DLL就不能与已经发布的程序兼容,就不能实现增量升级,即只用覆盖某个DLL,而不需要全部都要更新,废弃的接口删除会导致同样的问题。

综述:为什么作者的这个DLL能实现增量更新?

COM对象通过特定的导出方法在DLL中以new的方式创建,通过引用计数自动析构,客户端不能自己创建COM对象,COM对象的内部结构发生变化,对外部也没有影响,如果新增了接口,就在最后加,之前的接口在虚方法表中的位置不会受到印象,即对别的接口没有影响,废弃的接口不能删除,

改变对象的内存结构和新增virtual方法都没关系,那不就成了。实现增量不在是问题,我们在回到FastString这个问题上,如果FastString一开始是以上诉方式实现的,现在要新增一个len字段和一个Length接口,我就这样增了,新出个版本,直接覆盖以前的那个DLL,我直接可以用,一切都是OK的,外部的调用不会受到任何影响。为了证明这个FastString能实现增量升级,我做了一个DEMO,大家可以试一下,我就是下载地址

你或许会说我这说的都不是COM,但这的确是更好的C++。

我的博客目录

COM是一个更好的C++,布布扣,bubuko.com

时间: 2024-10-13 16:59:48

COM是一个更好的C++的相关文章

《COM本质论》COM是一个更好的C++心得分享

昨天看了<COM本质论>的第一章"COM是一个更好的C++",认为非常有必要做一些笔记,于是整理成这篇文章.我相信你值得拥有. 这篇文章主要讲的内容是:一个实现了高速查找功能的类FastString,在一个小小的需求之后,慢慢的演变成一个COM组件的过程. 类FastString实现了一个高速查找字符串的功能.快到时间复杂度是O(1),我们先无论作者是怎么实现的,预计是通过空间换时间. 因为这个类查找字符串非常快,于是作者就把这个类当做一个产品,以源代码的方式卖给须要的厂商

在Ubuntu 12.04 中如何更换一个更快的软件源?

在ubuntu 下下载安装软件使用不同的镜像源速度差异非常大, 官方的那个比较慢,所以选择最快的最有效率.设置方法不一,网上教程比较常见的是自己手动去更改更新源列表,把特定版本的源列表直接复制到 /etc/apt/sources.list 文件中,但是有时候源文件会失效,而且不一定每次源文件都是跟自己的版本兼容的. 网上看到一个教程,方法很简单, 转载一下:(转自于IMCN) 在Ubuntu 12.04 中用户如何更换一个更快的软件源?

我想成为一个更优秀的人

我想成为一个更优秀的人,对父母,对梦想,对生活,对自己. 大四,也算是正式进入了实习和工作阶段,开学后,找了份工作,挺轻松,每天完成自己的任务后可以有很多时间学习自学一些新东西,这对于现阶段的我来说还是挺不错的.作息规律也比在学校规律了很多,果然我还是适合一个人生活和规划,这样生活或许更有效率.这份在武汉的工作应该会坚持到明年春招或大学毕业,具体看我技术达到的程度,到了我觉得可以的程度,我想我也应该离开武汉,去北京闯闯了.为什么去北京?或许是因为北京是全国少有的一个你对别人谈梦想,别人不会骂你傻

如何选择一个更有利于网站建设与优化的主机

主机对网站优化的影响是显而易见的,一方面是体现在主机的性能好不好,会不会经常死机.访问速度快不快.打开一个网页需要多久.是不是支持对网站日志的读 写.支不支持URL重写操作:另一方面,体现在主机以往的履历,该主机上,其他网站是什么类型的,有没有包含被禁止的内容,出现在网站上,主机上被惩罚的 网站多不多等等.下面就来具体分析一下,建立网站,应该选择什么样的主机.一个完美的主机,对网站排名的重要性. 选择怎么样的主机? 要点一.主机的IP或者整个服务器是否有被惩罚过 分析:一个IP或者整个服务器上所

【摘】50个jQuery代码段帮助你成为一个更好的JavaScript开发者

今 天的帖子会给你们展示50个jQuery代码片段,这些代码能够给你的JavaScript项目提供帮助.其中的一些代码段是从jQuery1.4.2才 开始支持的做法,另一些则是真正有用的函数或方法,他们能够帮助你又快又好地把事情完成.这些都是我尽量记住的有着最佳性能的代码段,因此如果你发现你任 何可以做得更好的地方的话,欢迎把你的版本粘贴在评论中!我希望你在这一文章中能找到有帮助的东西. 1.     如何创建嵌套的过滤器: //允许你减少集合中的匹配元素的过滤器, //只剩下那些与给定的选择器

pytion3--class一个更实际的例子

class一个更实际的例子 到目前为止,我们所看的大多数例子都是人为创造而且是独立完备的,其目的是为了帮助你把注意力集中在基础知识上.然而,本章的结尾是一个较大的例子,把我们所学的大多数概念都聚合在这里.这个例子几乎是需要自行研究的练习题:试着看这个例子的程序代码,来了解方法调用是如何解析的. 简而言之,下列模块person.Py定义了三个类: GeneriCOisplay是混合类,提供了通用的__str__方法.对任何继承了它的类来说,这个方法会返回字符串,给出创建该实例的类的名称,以及实例内

自定义一个更好用的SwipeRefreshLayout(弹力拉伸效果详解)(转载)

转自: 自定义一个更好用的SwipeRefreshLayout(弹力拉伸效果详解) 前言 熟悉SwipeRefreshLayout的同学一定知道,SwipeRefreshLayout是android里面专为RecyclerView,NestedScrollView提供下拉刷新动画的一个控件.可是在使用过程中有些局限性,例如只支持上述控件,不支持ListView,GridView等,另外下拉的动画效果很难更改,而且不支持上拉加载--在很多场景的情况下往往不符合我们的需求. 今天为大家分享的是一个支

建立一个更高级别的查询 API:正确使用Django ORM 的方式(转)

add by zhj: 本文作者是DabApps公司的技术主管,作者认为在view中直接使用Django提供的ORM查询方法是不好的,我对此并不赞同,可能作者 写这篇文章是给Django的初学者看,所以在说明方法演进时有些罗嗦,至少方法1是没有必要说的. 本文介绍了如何给QuerySet类增加方法属性.作者写本文时,Django1.7还在开发中,没有发布.在Django1.7版本中提供了这个功能, 见https://docs.djangoproject.com/en/dev/releases/1

如何搭建一个更高效的用户反馈机制?

如果标题吸引你点击进来,说明你的 App 已经积累了一定数量的用户了.想知道你的用户如何评价你的 App?想了解他们的需求?往下瞧↓↓ 近两年,越来越多的 App 开发者开始重视用户反馈,但每天面对大量繁冗.无序的用户反馈,运营人员处理起来很费力,甚至出现很多“没人理”的情况.如何搭建一个更高效的用户反馈机制? 友盟用户反馈就致力于提供这样的服务,让 App 开发者和用户之间的交流更加简单和实时,沟通“零距离”.近日,友盟用户反馈发布他们的最新版本(Android 5.1 /iOS 2.1),除