Delphi container library X 库简介

与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类库大抵就是如此,对于程序中的数据结构的表示还是很有用处的。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DIContainers is a collection of more than 100 individual container classes for Delphi (Embarcadero, CodeGear, Borland). Four different container structures are available: Hash containers Doubly-linked lists N-ary trees Vector containers DIContainers is designed with easy customization in mind. Unlike other libraries, it strictly separates a containers' data and structure: Data describes an individual item in the container, for example an Integer number. The data layer is mostly responsible to manage the item's memory, but also for copying, comparing, and streaming of items. Data items in DIContainers are made up of memory efficient Pascal records. Special item handlers take care to initialize and finalize items automatically as required. Structure describes the arrangement of items within the container, like linked list, linear vector, etc. The structure determines how quickly items can be added, manipulated and retrieved from the container. Using different item handlers, the same structure can provide for quite different containers (see hierarchy on the right). To create a new container for some type of data, it is often sufficient to reuse an already existing item handler or to create a new item handler for the new type of data. Type. On top of the general container classes, there are many ready-made containers which interface typed access to their items like strings (WideStrings and AnsiStrings), different Number types, Objects, Pointer, and various combinations of the above. More than 100 of these containers are ready to use straight out of the box. Advanced container operations include cross-container assignment (i.e. from lists to vectors) and cascading streaming. The graphic to the right shows the class hierarchy of containers contained in DIContainers. Bold font marks important classes like item handlers and structure containers. Their descendant classes in regular font provide typed access to their items. The graphic was automatically created from the DIContainers library by one of the demo applications.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值