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.
Answer:
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)!
1 var
2 Childs: TSubTList;
3
4 for i := 0 to Childs.count - 1 do
5 BusinessClass(Childs[i]).Free;
6 Childs.Free
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.
Childs.List^[i];
You then takes responsability of making sure reading or writing can't be beyond the
ends of an array of the TList.
Example
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:
7 var
8 Childs: TSubTList; //or TBrokerList
9 Childs.Add(BusinessClass.create(self));
10
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.
14
15 type
16 TBrokerRec = record
17 intVal: integer;
18 strVal: string;
19 ptrStr: pChar;
20 end;
21 PBrok = ^TBrokerRec;
22
23 TBrokerList = class(TList)
24 protected
25 procedure freeElement(elem: PBrok);
26 function GetItems(Index: Integer): PBrok;
27 procedure SetItems(Index: Integer; item: PBrok);
28 public
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;
39 end;
40
41 TBrokerList
42
43 destructor TBrokerList.destroy;
44 begin
45 clear;
46 inherited Destroy;
47 end;
48
49 function TBrokerList.Add(Item: PBrok): Integer;
50 begin
51 result := inherited Add(Item);
52 end;
53
54 procedure TBrokerList.Delete(index: integer);
55 begin
56 freeElement(items[index]);
57 inherited delete(index);
58 end;
59
60 function TBrokerList.First: PBrok;
61 begin
62 result := inherited First;
63 end;
64
65 procedure TBrokerList.freeElement(elem: PBrok);
66 begin
67 if elem <> nil then
68 dispose(elem);
69 end;
70
71 function TBrokerList.indexOf(item: PBrok): Integer;
72 begin
73 result := inherited indexOf(item);
74 end;
75
76 procedure TBrokerList.Insert(index: integer; item: PBrok);
77 begin
78 inherited insert(index, item);
79 end;
80
81 function TBrokerList.Last: PBrok;
82 begin
83 result := inherited Last;
84 end;
85
86 procedure TBrokerList.pClear; //instead of Free from outer class
87 var
88 x: Integer;
89 begin
90 for x := 0 to count - 1 do
91 freeElement(items[x]);
92 inherited clear;
93 end;
94
95 function TBrokerList.Remove(item: PBrok): Integer;
96 begin
97 result := indexOf(item);
98 if Result <> -1 then
99 delete(result);
100 end;
101
102 function TBrokerList.GetItems(Index: Integer): PBrok;
103 begin
104 result := inherited get(index);
105 end;
106
107 procedure TBrokerList.SetItems(Index: Integer; item: PBrok);
108 begin
109 inherited put(index, item);
110 end;
|