与Java和C++相比,Delphi对容器的支持实在少得可怜。Java有强大的集合框架,C++更有STL,Delphi有什么呢,不就是TList几个小巧的列表类,而TCollection系列的类更多只是为了可视控件而存在的,真正意义上的容器类几乎没有。
一日在Google上随意的敲上Delphi Container字样,没想到竟搜到一个SourceForge的开源项目,它在主页上是这样写的:
DCLX(Delphi container library X)是一个免费的库,它提供了数组列表(ArrayList),链表(LinkedList),向量(Vector),哈希映射(HashMap),哈希集合(HashSet),数组集合(ArraySet),队列(Queue)和栈(Stack)等数据结构,它还提供了类似STL的算法(比如Apply, Found, CountObject, Copy, Generate, Fill, Reverse, Sort...)。
看到这一段描述,心里不禁暗喜,马上下载来看,经过一天的代码阅读,基本理解大概框架,虽然没有达到让人欣喜的地步,但也令人欣慰,于是决定写一篇文章。

请从这里下载该类库:http://sourceforge.net/projects/dclx

整个库大概分为三部分,一个是接口声明部分,一个是具体类实现部分,另一个是算法函数。该库是基于接口实现的,在DCL_intf单元中声明了所有的接口,确定了实现这些容器应该遵循的规范,每种容器类型的接口都声明三种类型,分别是接口,类,和字符串,比如Array类型的容器共有三个接口,分别是IIntfList/IStrList/IList,它们都提供了一个Items[]的方法。特别值得注意的还有Iterator和Cloneable接口,Iterator类型的接口提供了访问容器的方法,事实上对于容器的访问都是必须通过相应的Iterator来访问。而Cloneable类型的接口则提供一个Clone的方法让容器中的项可以拷贝。

有了这些接口,则具体的类只要实现这些接口,就可以实现相应的容器类型了。所有的具体类都继承自TAbstractContainer,可以想象,既然那些容器都是实现相应接口的,则它们最好是TInterfaceObject的子类,可以免去处理_ADDRef等方法的麻烦。看一下TAbstractContainer的父类,果然是从TInterfaceObject继承下来的。不过它还有一个责职,就是保证容器类的线程安全,这个责职是可选的,做法是将dcl.inc文件中的{ $DEFINE THREADSAFE}改为{$DEFINE THREADSAFE},这样它就有了线程安全了。那么它是怎么做到的呢,我觉得它的做法相当的巧妙,我们最好来看一下源代码。
TAbstractContainer类中有FCriticalSection: TIntfCriticalSection;就是这样个类实现了线程安全的,看一个具体的容器类的做法,比如下面代码:

{$IFDEF THREADSAFE}
var
CS: IInterface;
{$ENDIF}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF}
if FSize = FCapacity then
Grow;
FElementData[FSize] := AObject;
Inc(FSize);
Result := True;
end;

首先通过编译器指令判断是否打开了线程安全的开关,也即我们上面的修改Inc文件。如果有则声明一个IInterface接口,然后CS := EnterCriticalSection;这样就线程安全了,是不是有一些不解呢,有些Windows编程知识的人都可以推断它的线程安全肯定是通过临界区来实现的,但临界区至少应该是Enter然后Leave成对的啊,而上面的做法却只有Enter,没有Leave,难道它自动会被调用。没错,临界区的LeaveCirticalSection方法就是自动被调用的,关键就在于接口的生命周期自动管理。
看一下TIntfCriticalSection的声明,知道它实现了IInterface接口,并实现了该接口的三个方法。在类的构造函数中有InitializeCriticalSection(FCriticalSection);表明真的是用临界区,这是初始化代码。而_AddRef方法中是EnterCriticalSection(FCriticalSection); _Release方法中是LeaveCriticalSection(FCriticalSection);理解了吗?
我们再来分析上面那个方法的代码:首先调用EnterCriticalSection,它在父类中的实现如下:Result := FCriticalSection as IInterface;所以调用之后,编译器自动调用_AddRef方法,临界区就Enter了。而代码继承执行,到了该方法结束,编译器又会自动调用接口_Release,这时临界区就Leave了。
利用接口的自动管理实现线程安全,妙。

上面的基石做好了,接下来就是具体的容器类,这些容器的方法基本全是实现相应接口,重要的一点是容器中的项是通过动态数据组织起来的,在DCLUtil单元中有三个动态数组的声明:
type
TIInterfaceArray = array of IInterface;
TObjectArray = array of TObject;
TStringArray = array of string;

大部分的容器类的内部数据都通过这三个数组来包装的,Hash表比较特殊一点,不过也是动态数组。
我们可以拿TStrArrayList(在ArrayList单元中)来分析,看看它具体是怎么样实现字符串数组列表的。
它实现了这几个接口:IStrCollection, IStrList, IStrArray, ICloneable,
在构造函数中设置了初始的内存(也有另一个直接拷贝IStrCollection接口的数据):

SetLength(FElementData, FCapacity);
其中FElementData是TStringArray = array of string
对它进行增删的时候,都是对FElementData的操作,比如要移除一个元素:
Result := FElementData[Index];
FElementData[Index] := '';
System.Move(FElementData[Index + 1], FElementData[Index],
(FSize - Index) * SizeOf(IInterface));
Dec(FSize);

根本方法就是Move函数,对内存块的移动。那一句FElementData[Index] := ''使该元素的字符串引用计数减1,编译器才会正确的管理该字符串的生命周期。
看代码可以注意到,该类对外提供的方法只有构造函数和析构函数,而Add等方法却是保护的。这是因为访问和操作容器类都必须通过迭代子来完成,比如TStrItr,它实现了IStrIterator接口,负责访问TStrArrayList,在TStrItr的构造函数中传进TStrArrayList的实例,则以后对于TStrArrayList实例的操作都是通过TStrItr来完成,这就是迭代子模式的应用,而又有一些代理模式的感觉。

DCLX类库大抵就是如此,对于程序中的数据结构的表示还是很有用处的。

http://blog.csdn.net/linzhengqun/article/details/519543

浅析Delphi Container库(有开源的DCLX)的更多相关文章

  1. DELPHI PROTOBUF免费的开源支持库fundamentals5

    DELPHI PROTOBUF免费的开源支持库fundamentals5 1.源码URL: https://github.com/fundamentalslib/fundamentals5 2.编译P ...

  2. DELPHI优秀的一些开源框架:QDAC,MORMOT,DIOCP

    DELPHI优秀的一些开源框架:QDAC,MORMOT,DIOCP 程序员搞任何语言的程序开发上升到一定的层次,要想进步,必须要接触和学习使用优秀的开源框架. MORMOT封装了WINDOWS最新的H ...

  3. Delphi并行库System.Threading 之ITask 1

    不知什么时候,也许是XE8,也许是XE8之前 .Delphi里面多了个System.Threading的并行库. 虽然己经有非常棒的第三方并行库QWorker,但我还是更喜欢官方的东西. 下面是一段使 ...

  4. 编程 - 前端 - JavaScript - 库 - ECharts (开源可视化)

    ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等) ...

  5. Swifter.Json 可能是 .Net 平台迄今为止性能最佳的 Json 序列化库【开源】

    Json 简介 Json (JavaScript Object Notation) 是一种轻量级的数据交换格式.它作为目前最欢迎的数据交换格式,也是各大开源贡献者的必争之地,如:阿里爸爸的 fastj ...

  6. rtvue-lowcode:一款基于uniapp框架和uview组件库的开源低代码开发平台

    rtvue-lowcode低代码开发平台 rtvue-lowcode一款基于uniapp框架和uview组件库的低代码开发平台,项目提供可视化拖拽编辑器,采用MIT开源协议,适用于app.小程序等项目 ...

  7. delphi 微信(WeChat)多开源代码

    在网上看到一个C++代码示例: 原文地址:http://bbs.pediy.com/thread-217610.htm 觉得这是一个很好的调用 windows api 的示例,故将其转换成了 delp ...

  8. Azure Container Registry-基于开源 Docker Registry 的专用 Docker 注册表服务

    本文语雀知识库:https://www.yuque.com/seanyu/azure/acr 概述 Azure 容器注册表(Azrue Container Registry,简称ACR)是 Azure ...

  9. qt安装必要的库 qt开源安装包下载

    yum install mesa-libGL-devel mesa-libGLU-devel #yum install freeglut-devel http://www.qt.io/download ...

随机推荐

  1. DeDeCMS中如何实现下拉菜单

    在5.7版本,已经有比较简单的方法实现下拉菜单,我们可以用它已有方法,也可以用我写的第二种方法来实现 1. 在需要下拉菜单的地方加入以下代码 <div id="navMenu" ...

  2. 在Myeclipse中安装java Decompiler

    由于在myeclipse中的Help选项中没有Install New Software,所以在eclipse中安装插件的方法并不适应于Myeclipse,但是我们可以通过点击Windows->P ...

  3. 解说cocos2d-x几种画图方法的用法与思考

    CCRenderTexture 自己的理解 CCRenderTexture类似一张空白的“画布“,用户通过自定义笔刷(CCSprite*),在touch事件中把笔刷的移动痕迹“记录”起来,从而“画”出 ...

  4. HTML的表单元�

    HTML的表单元素 表单元素是同意用户在表单中(比方:文本域,下拉列表,单选框,复选框等等)输入信息的元素 表单标签 文本域(Text Fields) 当用户要在表单中键入字母,数字等内容时,就会用到 ...

  5. 总结NHibernate 中删除数据的几种方法

    今天下午有人在QQ群上问在NHibernate上如何根据条件删除多条数据,于是我自己就写了些测试代码,并总结了一下NHibernate中删除数据的方式,做个备忘.不过不能保证囊括所有的方式,如果还有别 ...

  6. HDU 2544 最短

    链接:http://acm.hdu.edu.cn/showproblem.php? pid=2544 解析: 首先数据量为V<=100 那么这里使用不论什么基础的最短路的算法都不会超时! 常见数 ...

  7. hdu 3874 Necklace(线段树)

    这道题目和我之前做过的一道3xian大牛出的题目很像,不过总的来说还是要简单一点儿. 计算区间内的值的时候如果两个值相等,只能计算其中一个. 这道题需要将所有的问题输入之后再计算,首先,对所有问题的右 ...

  8. [置顶] NS2中TCP拥塞控制仿真过程中盲点解析

    最近利用NS2做TCP拥塞控制协议的仿真,发现很多变量的方法含义都是解释的不清楚,给核心模块修改带来很多麻烦,所以决定用最准确的语言解释成员变量.方法,术语等的含义.限于个人水平,若有错误请留言指正! ...

  9. 矩形、占位符组件——axure线框图部件库介绍

    矩形组件和占位符没有太多的区别,这里我们主要讲解矩形组件的操作和使用,占位符的操作各位可以按照矩形的操作方法进行练习一下. 矩形组件是一个矩形,它可以用来做很多的工作,比如页面上需要一块蓝色的背景,就 ...

  10. xxx==null和xxx.equals(null)的区别

    如果xxx不是null的话,xxx==null将返回false,如果xxx是null的话,xxx将返回ture 而对xxx.equals(null)而言,他将永远返回false,因为如果xxx不是nu ...