浅析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
浅析Delphi Container库(有开源的DCLX)的更多相关文章
- DELPHI PROTOBUF免费的开源支持库fundamentals5
DELPHI PROTOBUF免费的开源支持库fundamentals5 1.源码URL: https://github.com/fundamentalslib/fundamentals5 2.编译P ...
- DELPHI优秀的一些开源框架:QDAC,MORMOT,DIOCP
DELPHI优秀的一些开源框架:QDAC,MORMOT,DIOCP 程序员搞任何语言的程序开发上升到一定的层次,要想进步,必须要接触和学习使用优秀的开源框架. MORMOT封装了WINDOWS最新的H ...
- Delphi并行库System.Threading 之ITask 1
不知什么时候,也许是XE8,也许是XE8之前 .Delphi里面多了个System.Threading的并行库. 虽然己经有非常棒的第三方并行库QWorker,但我还是更喜欢官方的东西. 下面是一段使 ...
- 编程 - 前端 - JavaScript - 库 - ECharts (开源可视化)
ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等) ...
- Swifter.Json 可能是 .Net 平台迄今为止性能最佳的 Json 序列化库【开源】
Json 简介 Json (JavaScript Object Notation) 是一种轻量级的数据交换格式.它作为目前最欢迎的数据交换格式,也是各大开源贡献者的必争之地,如:阿里爸爸的 fastj ...
- rtvue-lowcode:一款基于uniapp框架和uview组件库的开源低代码开发平台
rtvue-lowcode低代码开发平台 rtvue-lowcode一款基于uniapp框架和uview组件库的低代码开发平台,项目提供可视化拖拽编辑器,采用MIT开源协议,适用于app.小程序等项目 ...
- delphi 微信(WeChat)多开源代码
在网上看到一个C++代码示例: 原文地址:http://bbs.pediy.com/thread-217610.htm 觉得这是一个很好的调用 windows api 的示例,故将其转换成了 delp ...
- Azure Container Registry-基于开源 Docker Registry 的专用 Docker 注册表服务
本文语雀知识库:https://www.yuque.com/seanyu/azure/acr 概述 Azure 容器注册表(Azrue Container Registry,简称ACR)是 Azure ...
- qt安装必要的库 qt开源安装包下载
yum install mesa-libGL-devel mesa-libGLU-devel #yum install freeglut-devel http://www.qt.io/download ...
随机推荐
- Android XML解析器的问题
最近在项目中遇到了一个解析XML的问题,我们是用android自带的DOM解析器来解析XML的,但发现了一个android的问题,那就是在2.3的SDK上面,无法解析像<, >, 等字符串 ...
- Ch04 充满动作的控制器
4.1 考察控制器和动作 4.1.1 IController与控制器基类 4.1.2 如何形成动作方法 4.2 哪些应该放在动作方法中 4.2.1 手动映射视图模型 4.2.2 输入验证 ...
- 基于visual Studio2013解决C语言竞赛题之1044数组处理
题目 解决代码及点评 /* 分别输入N和M个数到数组A和数组B中.把只在其中一个数组中出现的那些数送入到数组C中,并指出它们在A或B数组中的位置. */ #include <s ...
- 73_leetcode_Construct Binary Tree from Inorder and Postorder Traversal
Given inorder and postorder traversal of a tree, construct the binary tree 1:中序和后序遍历构成一棵树.2:採用递归的方法. ...
- 【图像处理】Gabor过滤器
Gabor内核参考wiki 使用实数Real的公式计算核函数代码: Mat getGaborFilter(float lambda, float theta, float sigma2,float g ...
- WinFrom - DataGridView控件右键选中记录并弹出菜单
dataGridView右键菜单并选中该行 程序代码: private void dataGridView1_CellMouseDown(object sender, DataGridViewCell ...
- element.style覆盖了我的样式!!
原文:element.style覆盖了我的样式!! 有时候在写css时,显示效果会出现非常诡异的效果 不知道有没有遇到这种 css: #logo{ border: solid 1px blue; } ...
- tomcat 后台启动设置
如果你环境变量配置对了且保证tomcat的bin目录下有如下三个文件,则你只需在cmd中运行 service install tomcat即可将tomcat添加的服务项中,然后在设置开机自动启动,则以 ...
- css3 动画运动路径
1.cubic-bezier贝塞尔曲线CSS3动画工具 http://www.xuanfengge.com/cubic-bezier-bezier-css3-animation-tools.html ...
- 我在北京找工作(五):备战阿里巴巴java笔试<1>:筑基
@@@2013年9月11日 还在北京昌平区@@@ 好几天没有往博客上贴我的面试备战笔记了,今天开始分享一下备战阿里巴巴校招的笔经,当然重点是java方向的题目~. 插一段2014年阿里巴巴校招的消息: ...