Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Class TList

I've created a simple class I keep the Objects inside a Generic list . I managed to get it working . But I do not understand why this is not working .

for Monster in MonsterList do
    begin
      Monster.Free;
      MonsterList.Remove(Monster);
    end;

If I try to Free and Remove items like this from the MonsterList , it does not really remove , in my case , the names disappeared but the Strength values stayed there . If I tried to list the contents of the MonsterList afterwards I always had one item left . So I googled a bit , and found here on Stack-overflow that I good solution would be to simply count downto .

Another thing is when I add the Monsters to the MonsterList , I add 3 Items , but if I Debug I see the MonsterList has Actually 0,1,2,3 3 is NULL this only happens if I add all three, if I only add two objects it does not create a final NULL pointer . Is this some sorta Optimization kicking in ?

The Entire Code ( not much )

unit MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Generics.Collections, Generics.Defaults,
  System.Types;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMonster = class
    private
      fName : string;
      fStrength : integer;
      fisDead : boolean;
    public
      constructor Create(Name : string; Strength : integer);
      destructor Destroy; override;

      property Name : string read fName write fName;
      property Strength : integer read fStrength write fStrength;
      property isDead : boolean read fisDead write fisDead;
  end;

var
  Form1: TForm1;
  MonsterList : TList<TMonster>;
  MonsterInstances : integer = 0;


implementation

{$R *.dfm}

constructor TMonster.Create(Name: string; Strength: integer);
begin

  inc(MonsterInstances);

  fName := Name;
  fStrength := Strength;
  fisDead := false;
end;

destructor TMonster.Destroy;
begin
  dec(MonsterInstances);

  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
var Monster : TMonster;
    i : integer;
begin
  MonsterList := TList<TMonster>.Create;

  Memo2.Lines.Add(inttostr(MonsterInstances));

  MonsterList.Add(TMonster.Create('Jill',10));
  MonsterList.Add(TMonster.Create('Jane',1));
  MonsterList.Add(TMonster.Create('Rob',20));

  Memo2.Lines.Add(inttostr(MonsterInstances));

  for Monster in MonsterList do
    begin
      Memo1.Lines.Add(Monster.fName+ ' Strenght '+inttostr(Monster.fStrength)+' IsDead= '+booltostr(Monster.fisDead))
    end;

  MonsterList[1].isDead:=true;

  // not working
  {for Monster in MonsterList do
    begin
      Monster.Free;
      MonsterList.Remove(Monster);
    end; }

  // works
  for i := MonsterList.Count-1 downto 0 do
    begin
      if MonsterList[i].isDead = true then
        begin
          MonsterList[i].Free;
          MonsterList.Delete(i);
          MonsterList.Capacity:=MonsterList.Capacity-1;
        end;
    end;

  Memo1.Lines.Add('Survivors :');

  for Monster in MonsterList do
    Memo1.Lines.Add(Monster.Name+' Strenght '+inttostr(Monster.Strength));


  ShowMessage(inttostr(MonsterInstances));

end;

end.

Thank you!

like image 292
user1937012 Avatar asked Jan 02 '23 06:01

user1937012


1 Answers

You can't modify a list while iterating over it like that. Instead, free all the members, and then clear the list.

for Monster in MonsterList do
  Monster.Free;
MonsterList.Clear;

This has the added advantage of not calling Remove which spends time searching for the item.

Perhaps simpler would be to use TObjectList<T> and allow the collection to manage life time of its members. Then you can simply call Clear and so long as OwnsObjects is True all the members will be destroyed and the list cleared.

As far as your second issue goes, if you add three items, there are items with index 0, 1 and 2. There is no item with index 3. Now, internally the collection may well use an internal array that is over allocated. And so that private internal array can have an index 3. But the contents of that internal array should not matter to you.

like image 108
David Heffernan Avatar answered Jan 11 '23 22:01

David Heffernan