与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. linux+nginx+mysql+php高性能服务器搭建

    1.安装基础包 yum -y install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freet ...

  2. 03-OC实例方法、内存管理

    目录: 一.实例方法和类方法 二.工厂方法 三.self 四.编程规范 五.内存管理 回到顶部 一.实例方法和类方法 1 只能通过实例调用的方法叫实例方法."-" 2 只能通过类调 ...

  3. lokijs

    http://lokijs.org/#/ 500,000+ 1.1M ops/s. A fast, in-memory document-oriented datastore for node.js, ...

  4. Introduction to the Build Lifecycle

    Introduction to the Build Lifecycle Table Of Contents Build Lifecycle Basics Setting Up Your Project ...

  5. GDSOI2015 task2 覆盖半径

    题目大意 一个\(n\times m\)的矩阵中有\(p\)个已经确定圆心的圆,并且每个格子有一定的分数,如果一个格子被任意一个或以上的圆覆盖,那么就可以得到这个格子的分数.现在求最小的半径,使得得分 ...

  6. dfs-hdu-4620-Fruit Ninja Extreme

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4620 题目大意: 切水果.给n刀,每刀的时间,每刀切的水果的种类.求能切的最多的刀数,使得每相邻的两 ...

  7. 不用SWIG,Go使用C++代码的方式

    将C++代码用C作一次封装,就可以让Go调用了. 这是一个C++头文件: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #ifndef CGO_CPPGO_C ...

  8. delphi高手突破学习笔记之面向对象类和对象的本质(有汇编解释 good)

    知识点1:堆和栈 每个应用程序可以获得的内存空间分为两种:堆(heap)和栈(stack). 堆又称为“自由存储区”,其中的内存空间的分配与释放是必须由程序员来控制的.例如,用GetMem函数获取了一 ...

  9. 图示CCScrollView的相关概念

    (转载请注明原文地址:http://blog.csdn.net/while0/article/details/11527899) 见下图: 1)设置ScrollView的视口大小的函数是:setVie ...

  10. 类作为返回类型 ,具有java特点-封装等 而且应用起来很方便。

    public class StudentDao { public Student getStudent(){ Student stu = new Student(); stu.setName(&quo ...