浅析Delphi Container库(有开源的DCLX)

与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

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

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

主要介绍当前主流的激光雷达数据处理库、开源软件、商业软件等

1.主要介绍当前主流的激光雷达数据处理库.开源软件.商业软件等. 英文主页: http://opentopo.sdsc.edu/tools/listTools 2.日期 工具名称 工具类型 关键词 评分 2017年1月12日 planlauf /地形 说明: planlauf / TERRAIN是Windows 7/8/10易于使用的应用程序,可视化数字高程模型.它使用游戏行业的最先进的技术,如"网格抽取","正常映射"和"细节水平",以显着减小

Delphi并行库System.Threading 之ITask 1

不知什么时候,也许是XE8,也许是XE8之前 .Delphi里面多了个System.Threading的并行库. 虽然己经有非常棒的第三方并行库QWorker,但我还是更喜欢官方的东西. 下面是一段使用System.Threading中ITask的代码 procedure TForm3.SpeedButton1Click(Sender: TObject); var tasks: array of ITask; value: Integer; LTask:ITask; X,Y:INTEGER; b

浅析live555媒体库之入门编译使用

简介 Live555 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如RTP/RTCP.RTSP.SIP等的支持.Live555实现了对多种音视频编码格式的音视频数据的流化.接收和处理等支持,包括MPEG.H.263+.DV.JPEG视频和多种音频编码.同时由于良好的设计,Live555非常容易扩展对其他格式的支持. 主要分支 live555主要包括四个比较重要的库,分别为 UsageE

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

Json 简介 Json (JavaScript Object Notation) 是一种轻量级的数据交换格式.它作为目前最欢迎的数据交换格式,也是各大开源贡献者的必争之地,如:阿里爸爸的 fastjson(java),腾讯的 rapidjson(c++) 等.但 .Net 却没有得到大厂的青睐,在 Swifter.Json 之前 .Net 的 Json 解析库都不完美. Swifter.Json 简介 Swifter.Json 是 .Net 平台上一个功能强大,简单易用,稳定及高性能的 Jso

delphi 微信(WeChat)多开源代码

在网上看到一个C++代码示例: 原文地址:http://bbs.pediy.com/thread-217610.htm 觉得这是一个很好的调用 windows api 的示例,故将其转换成了 delphi xe8 源码下载:OpenNewWechat 下面给出所用到的windows api 定义 unit uWinApi; // http://bbs.pediy.com/thread-217610.htm // 微信(WeChat)电脑端多开分析+源码 { 感谢原文提供的代码和 exe 晓不得2

浅析live555媒体库之工作流程介绍

live555项目的源代码包括四个基本的库,各种测试代码以及Media Server. 工作模块 四个基本的库分别是: UsageEnvironment  TaskScheduler, groupsock, liveMedia和BasicUsageEnvironment. 官网英文的基本介绍截图如下: 虽是英文的,但是难度不大,能看懂大致意思.这里多说一句,程序员还是要接触并学习英文,毕竟好的技术都是国外引进的.简单说下我的理解 UsageEnvironment   该类库是对系统环境的抽象,包

关于32位和64位部署出现C#调用delphi动态库DLL不成功的问题

由于项目中调用了动态库,这些动态库放在C:\Windows\System32下面,但是当部署到了64位的机器上可能就有问题了,最近这个问题就纠结了半天,在本机32为系统上测试动态调用ddl成功了,部署到64位Window Server2008上面也没问题,可是为什么到了64位的WIN7系统上出了问题呢?        最后才发现原来原来是DLL的位置不对,那么DLL到底该放到那呢?        如果细心的你,你会发现在C:\Windows\目录下面有两个文件夹:System目录和System3

浅析live555媒体库之实现实时码流预览

前面已经介绍了,通过live555来实现媒体文件的播放.这篇主要和大家说一下实时流的通过live555的播放. 相对之前的文件流,这里实时流只需要多实现一个子类:通过继承RTSPServer类来实现一些自己的相关操作. 如:有客户端请求过来的时候,需要先通过lookupServerMediaSession找到对应的session,这里可以定义自己的streamName,也就是url后面按个串,.如果没有找到,则新建生成自己需要的不同的session,还有填充自己的SDP信息等等操作. 继承RTS

【翻译】在GitHub上通过星级评估排名前10的最受欢迎的开源Delphi项目

GitHub上有相当多的Delphi开源项目可以为你节省一些时间.我在GitHub上搜索了Delphi,然后按最主要的项目进行排序,并列出了前十名单.这里有一些非常好的东西,包括Awesome Delphi(这是另一个顶级列表),Delphi IDE颜色主题项目,MVC Web框架,REST API客户端,Delphi开源计算机视觉库标题,一个非常强大的自定义TreeView组件,单元测试框架,将Chrome嵌入到您的应用中的组件,以及最终的OmniThreadLibrary. 查看下面的完整列