Author: Tomas Rutkauskas
How to store records in a TList when their number is unknown until runtime
Answer:
To store a number of records ( probably number unknown until runtime ), one would
use a Delphi TList object. TList is basically an array of pointers that grows as
needed, up to 16K pointers can be stored in a TList. It will accept anything that
even remotely looks like a pointer (a pointer is an address, normally of a bit of
data that has been allocated from the heap, and needs 4 bytes to store the
address). If you work with dynamically allocated data items you need to take care
of releasing this memory to the system heap again if it is no longer needed. It is
easy to forget this, especially if the data items are kept in a list. It is thus a
good idea to derive a custom list class from TList that takes care of freeing the
memory for the items it stores automatically.
1 type2 TRecord = record{ the record type }3 { ... }4 end;
5 PRecord = ^TRecord; { pointer type for pointers to TRecords }6 TRecordList = class(TList) { a customized version of TList to hold PRecord 7 pointers }8 private9 procedure SetRecord(index: Integer; Ptr: PRecord);
10 function GetRecord(index: Integer): PRecord;
11 public12 procedure Clear;
13 destructor Destroy; override;
14 property Records[i: Integer]: PRecord read GetRecord write SetRecord;
15 end;
16 17 {Methods of TRecordList}18 19 procedure TRecordList.SetRecord(index: Integer; Ptr: PRecord);
20 var21 p: PRecord;
22 begin23 { get the pointer currently in slot index }24 p := Records[index];
25 if p <> Ptr then26 begin27 { if it is different from the one we are asked to put into this slot, check if 28 it is <> Nil. If so, dispose of the memory it points at! }29 if p <> nilthen30 Dispose(p);
31 { store the passed pointer into the slot }32 Items[index] := Ptr;
33 end;
34 end;
35 36 function TRecordList.GetRecord(index: Integer): PRecord;
37 begin38 { return the pointer in slot index, typecast to PRecord }39 Result := PRecord(Items[index]);
40 end;
41 42 procedure TRecordList.Clear;
43 var44 i: Integer;
45 p: PRecord;
46 begin47 { dispose of the memory pointed to by all pointers in the list that are not Nil }48 for i := 0 to Pred(Count) do49 begin50 p := Records[i];
51 if p <> nilthen52 Dispose(p);
53 end;
54 { call the Clear method inherited from TList to set Count to 0 }55 inherited Clear;
56 end;
57 58 destructor TRecordList.Destroy;
59 begin60 { clear the list to dispose of any pointers still stored first }61 Clear;
62 inherited Destroy;
63 end;
All we did up to here was declaring types, lets put them to use now. First we need
an instance of TRecordList to store pointers to dynamically allocated records in.
That may be a field in a form, for example. Code to create and destroy the list has
to be added to the forms OnCreate and OnDestroy handlers.
64 { in a forms public section: }65 RecordList: TRecordList;
66 67 { in the forms OnCreate handler }68 RecordList := TRecordList.Create;
69 70 { in the forms OnDestroy handler }71 RecordList.Free;
72 73 74 //To add a record to the list you use code like this:75 76 77 var78 Ptr: PRecord; { local variable in a method }79 80 New(Ptr); { allocate a record on the heap }81 with Ptr^ do82 begin{ note the caret to dereference the pointer }83 { put data into the fields of the record }84 end;
85 recordIndex := RecordList.Add(Ptr);
You do this sequence for each record you need to store. Each record now resides at a specific slot in the list and you can access it via the index of this slot. Indices start at 0 and run to RecordList.Count-1.