Reputation: 385
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!
Upvotes: 4
Views: 2178
Reputation: 613572
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.
Upvotes: 9