Using pointers and TLists in Delphi
Pointers are an extremely powerful feature of Delphi, and when used correctly can be very useful. Many Object Pascal developers will turn lesser experienced programmers away from the use of pointers, as they can cause memory leaks or even program crashes when not managed properly. The aim of this article is to show how to use pointers properly and the benefits they provide when combined with the fantastic TList component.
The thing I use the pointer/TList combination for the most is creating and managing form objects at runtime, in this example I will show you how to create and destory some checkboxes.
First off we create a standard form application with 2 TButtons and a TPanel, the buttons will control adding and deleting the checkboxes and the panel will be the parent.
type TMyRecord = record c: TCheckBox; s: String; end;
Above is the custom type we will be using, a standard record type with a TCheckbox and a string.
We also need to create our TList in the form's OnCreate event.
procedure TForm1.FormCreate(Sender: TObject); begin items := TList.Create; end;
The next step is to create the first button's OnClick event, this is where we will create our new pointer, using the record above.
procedure TForm1.Button1Click(Sender: TObject); var n: ^TMyRecord; begin (* Adds a new checkbox to Panel 1 *) new(n); n^.c := TCheckBox.Create(Panel1); n^.c.Parent := Panel1; n^.c.Caption:='Tick me!'; n^.c.Align := alTop; n^.c.OnClick := @checkBoxClick; n^.s := 'Checkbox number ' IntToStr(items.Count 1); items.Add(n); end;
The two things to note above are the use of the ^ operator, this is how we define our type as a pointer and the second is the new() function, this creates the memory space to hold the new pointer.
Once the new pointer is created, we can continue to create our TCheckBox in the normal way, after that is created we assign a value to the string from our type and finally we add it to our TList.
You will note I have assigned an OnClick event to the created TCheckBox, I will use this to demonstrate how to determine which TCheckBox has been clicked.
procedure TForm1.checkBoxClick(Sender: TObject); var x: integer; n: ^TMyRecord; begin for x := 0 to items.Count -1 do begin n := items[x]; if Sender as TCheckBox = n^.c then begin showmessage(n^.s); break; end; end; end;
So all we do is simply loop through our TList until the TCheckBox matches Sender and then use showmessage() on the string value from the type (note again the use of the ^ operator). If this procedure was being shared over different components we could use the is function to check the type before we use as.
The final thing to do is to delete our created items, when Button2 is clicked it will loop through the TList and delete any checkboxes that are ticked.
procedure TForm1.Button2Click(Sender: TObject); var x: integer; n: ^TMyRecord; begin (* Deletes any ticked checkboxes *) for x := items.Count-1 downto 0 do begin n := items[x]; if n^.c.Checked then begin n^.c.Free; items.Delete(x); dispose(n); end; end; end;
There are 3 things to note here, firstly we count down from items.Count-1 to 0, if we were to count up from 0 we would run into a list index out of bounds error! It is important to free the TCheckBox before deleting it from the TList, not doing so would cause a memory leak. The final thing is to call dispose() on the pointer variable to free up the pointer memory.
And that's it! Pointers aren't that scary after all. Download link for the full source code can be found at the top of this page, the archive contains both a Delphi and Lazarus project.blog comments powered by Disqus