Author: Max Kleiner
You can use a TList almost for everything, so an own class leads to better design
and maintainability therefore the article shows how and why.
A certain view is that the TList class in Object Pascal (OP) is not a class from
which we can descend, so the choice lies between subclassing (inheritance) or
delegation (means create a separate class which holds the TList instance). But you
can combine the two OO-technologies, especially you have multiple objects to store:
Subclass the TList that exposes only function equivalents of TList
Create a separate class that uses a TList instance
Some Advantages and Tricks of TList:
TList, which stores an dynamic array of pointers, is often used to maintain lists
of objects or records. TList introduces properties and methods to
Add or delete the objects in the list.
Rearrange the objects in the list.
Locate and access objects in the list.
Sort the objects in the list.
The Items of a TList are numbered from 0 to Count-1, that means zero based. Above
D5 and Kylix, Borland changed the operation of TList with the introduction of a new
descendant called TObjectList. They changed only the mechanism of freeing objects
in a TList.
If the OwnsObjects property of a TObjectList is set to True (the default),
TObjectList controls the memory of its objects (by a new virtual method Notify),
freeing an object when its index is reassigned or or when the TObjectList instance
is itself destroyed, but the more items in the TList, the longer it takes. The
worse is that a TList gets slower, so write always like in the following example
your own Free-method (as it was with pre-Delphi 5 TList)!
2 Childs: TSubTList;
4 for i := 0 to Childs.count - 1 do
BusinessClass(Childs[i]).Free calls every object on the list and frees the memory
of every object or record that we add on the list.
Then Child.Free calls Destroy and then it calls Clear of TList but Clear only
empties the Items array and set the Count to 0. Clear frees the memory used to
store the Items array and sets the Capacity to 0. Be care about Delete, Delete does
not free any memory associated with the item.
Gain speed with TList
The TList Sort mechanism is implemented with a quicksort algorithm, means we're
fast enough, but how about the access?
The normal way of accessing an object or item in a TList is the Items property in a
default manner like theList[i]. The performance problem is the reading or writing,
cause the compiler in OP inserts code to call getter or setter-methods, like
theList.get[i] which checks the index between 0 and Count -1. If we want gain speed
and get rid of the getter/setter we can call direct a variable of type PPointerList
(named List), but no validation takes place.
You then takes responsability of making sure reading or writing can't be beyond the
ends of an array of the TList.
The subclassing is like a wrapper class with simle one-line calls to the
corresponding methods of the inherited TList without typecasts. The example shows
how to add a record but with an object you have to change only the type and instead
of Dispose use Free.
The Method Add always inserts the Item pointer at the end of the Items array, even
if the Items array contains nil pointers:
8 Childs: TSubTList; //or TBrokerList
11 //Not all of the entries in the Items array need to contain references to objects.
12 Some of the entries may be nil pointers. to remove the nil pointers and reduce the
13 size of the Items array to the number of objects, call the Pack method.
16 TBrokerRec = record
17 intVal: integer;
18 strVal: string;
19 ptrStr: pChar;
21 PBrok = ^TBrokerRec;
23 TBrokerList = class(TList)
25 procedure freeElement(elem: PBrok);
26 function GetItems(Index: Integer): PBrok;
27 procedure SetItems(Index: Integer; item: PBrok);
29 destructor destroy; override;
30 function Add(Item: PBrok): Integer;
31 procedure Delete(index: integer);
32 function First: PBrok;
33 function indexOf(item: PBrok): Integer;
34 procedure Insert(index: integer; item: PBrok);
35 function Last: PBrok;
36 procedure pClear;
37 function Remove(item: PBrok): Integer;
38 property Items[Index: Integer]: PBrok read GetItems write SetItems;
43 destructor TBrokerList.destroy;
46 inherited Destroy;
49 function TBrokerList.Add(Item: PBrok): Integer;
51 result := inherited Add(Item);
54 procedure TBrokerList.Delete(index: integer);
57 inherited delete(index);
60 function TBrokerList.First: PBrok;
62 result := inherited First;
65 procedure TBrokerList.freeElement(elem: PBrok);
67 if elem <> nil then
71 function TBrokerList.indexOf(item: PBrok): Integer;
73 result := inherited indexOf(item);
76 procedure TBrokerList.Insert(index: integer; item: PBrok);
78 inherited insert(index, item);
81 function TBrokerList.Last: PBrok;
83 result := inherited Last;
86 procedure TBrokerList.pClear; //instead of Free from outer class
88 x: Integer;
90 for x := 0 to count - 1 do
92 inherited clear;
95 function TBrokerList.Remove(item: PBrok): Integer;
97 result := indexOf(item);
98 if Result <> -1 then
102 function TBrokerList.GetItems(Index: Integer): PBrok;
104 result := inherited get(index);
107 procedure TBrokerList.SetItems(Index: Integer; item: PBrok);
109 inherited put(index, item);