Delphi类TList

4

我创建了一个简单的类,将对象存储在一个通用列表中。我已经成功地使它工作了。但是我不明白为什么它不起作用。

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

如果我尝试从MonsterList中像这样释放和删除项目,它实际上并没有被删除,在我的情况下,名称消失了,但力量值仍然存在。如果我之后尝试列出MonsterList的内容,我总是会剩下一个项目。所以我在谷歌上搜索了一下,在Stack-overflow上发现一个好的解决方案就是简单地倒数计算。
另一件事是当我将怪物添加到MonsterList中时,我添加了3个项目,但如果我调试,我会看到MonsterList实际上是0,1,2,3 3是NULL。只有当我添加所有三个对象时,它才会创建最终的NULL指针。这是否是某种优化机制在起作用?
整个代码(不多)
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.

谢谢你!


2
在循环中从开头删除列表项绝不是一个好主意。 - LU RD
2
在处理类时,建议您考虑使用TObjectList而不是TList,这样您就不必费心释放Items。无论如何,如果没有特定的原因,您都不应该干涉列表的容量。 - Sebastian Proske
1个回答

9

您不能像那样在迭代列表时修改它。相反,释放所有成员,然后清除列表。

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

这样做的额外好处是不会调用Remove,从而节省了查找该项所需的时间。

也许更简单的方法是使用TObjectList<T>并允许集合管理其成员的生命周期。然后,您只需调用Clear,只要OwnsObjectsTrue,所有成员都将被销毁,并清除列表。

至于您的第二个问题,如果添加了三个项,则有索引为0、1和2的项。没有索引为3的项。现在,内部集合可能使用过度分配的内部数组。因此,该私有内部数组可以具有索引3。但是,该内部数组的内容对您来说不重要。


ObjectList看起来比List更有前途。谢谢! - user1937012
1
请记住,如果列表将拥有对象,则在调用Remove或Delete时它将自动销毁这些对象 - 如果您想避免这种情况,必须使用Extract。 - Stefan Glienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接