Delphi,如何在 TList 中释放记录

Posted

技术标签:

【中文标题】Delphi,如何在 TList 中释放记录【英文标题】:Delphi, how to free record in TList 【发布时间】:2015-08-24 19:02:09 【问题描述】:

我想知道释放充满记录的 TList 的最佳方法

我有以下记录:

type
TPkBill = record
    PkBill: integer;
    Constructor Create(c_PkBill: integer);

constructor TPkBill.Create(c_PkBill: integer);
begin
    PkBill := c_PkBill;
end;

我创建列表并用记录填充它:

procedure TfrmProject.lvBillDblClick(Sender: TObject);
var   
   i, iCount: integer;
   item: TListItem;
   oPkBill: TPkBill;
   lstPkBill: Tlist;
begin
   iCount := 0;
   lstPkBill:= TList.Create;

   //if an item is selected in lv
   if (lvBill.ItemIndex = -1) then begin exit; end
   else
   begin
        //Loop through all items and get selected item
        for i := 0 to lvBill.Items.Count - 1 do
        begin
            item := lvBill.Items.Item[i];
            if(item.Selected = true)then
            begin
                //create new item
                oPkBill := TPkBill.Create(StrToInt(lvBill.Items[i].Caption));
                //add it to a list
                lstPkBill.Add(TObject(oPkBill)); 
                //add up
                iCount := iCount +1;      
            end;
        end;
        //Now we have a list ok pkBill

        if(iCount > 1)then //other stuff I do
    end

我希望能够释放 TList 并释放记录。

这是我已经尝试过的:

for i := 0 to lstPkBill.Count - 1 do
begin
    //TObject(TPkBill(lstPkBill[i])).Free;  //Acces violation at adress..
    //FreeMem(TPkBill(lstPkBill[i]));   //Incompatible types
    //FreeMem(TObject(lstPkBill[i]));   //Incompatible types
end;
lstPkBill.Clear;
FreeAndNil(lstPkBill);

感谢您的帮助,不胜感激!

【问题讨论】:

除了调用 record 构造函数不会像 class 构造函数那样在堆上分配记录。此代码将基于堆栈的 记录实例放入TList。没有什么可以释放的。 您的代码只能在 32 位平台上编译,因为它依赖于 SizeOf(TPkBill) = 4 的事实。我敢打赌你没有意识到这一点。 记录构造器是撒旦的作品,永远不应该被使用。他们让你认为你已经在堆上分配了一些东西,或者需要调用Free。假装你从未听说过它们。 @DavidHeffernan 我什至不知道什么是 32 位平台,但我会查一下,谢谢!在 TLama,感谢您的提议,但我尝试了 RemyL。回答,它奏效了。但我敢打赌它也可以做到这一点! 如果你可以使用泛型,你应该这样做。你的代码会更简单,更安全。不易泄漏。雷米的回答很好,也很直接,但它可能不是解决你潜在问题的最佳方法。 【参考方案1】:

您已经使用构造函数定义了一条记录。调用记录构造函数不会像类构造函数那样在堆上分配内存。您的 oPkBill 变量存在于堆栈中。调用oPkBill := TPkBill.Create(...) 只会填充该变量的成员。然后,您将整个变量(仅包含一个 Integer 成员)类型转换为 TObject 指针。您实际上并未在堆上为列表项分配任何内存,因此无需释放它们。

我怀疑你实际上想要做的更像是这样的:

type
  PPkBill = ^TPkBill;
  TPkBill = record
    PkBill: integer;
    Constructor Create(c_PkBill: integer);
  end;

constructor TPkBill.Create(c_PkBill: integer);
begin
  PkBill := c_PkBill;
end;

procedure TfrmProject.lvBillDblClick(Sender: TObject);
var   
  i: Integer;
  item: TListItem;
  oPkBill: PPkBill;
  lstPkBill: TList;
begin
  if lvBill.ItemIndex = -1 then Exit;

  //an item is selected in lv

  lstPkBill := TList.Create;
  try
    //Loop through all items and get selected items
    for i := 0 to lvBill.Items.Count - 1 do
    begin
      item := lvBill.Items.Item[i];
      if item.Selected then
      begin
        //create new item
        New(oPkBill);
        try
          oPkBill^ := TPkBill.Create(StrToInt(lvBill.Items[i].Caption));
          //add it to a list
          lstPkBill.Add(oPkBill);
        except
          Dispose(oPkBill);
          raise;
        end;
      end;
    end;

    //Now we have a list ok pkBill
    if (lstPkBill.Count > 1) then
    begin
      //other stuff I do
    end;
  finally
    for i := 0 to lstPkBill.Count - 1 do
      Dispose(PPkBill(lstPkBill[i]));
    lstPkBill.Free;
  end;
end;

【讨论】:

感谢您的快速回复!它与 `PPkBill = ^TPkBill;` 一起工作得非常好,我知道我没有选择最好的技术和解释。有时我仍然对如何处理事情有些困惑,所以我喜欢从有经验的程序员那里得到答案 =) 再次感谢您的澄清! 是的,它运行良好,但你真的明白为什么以及为什么你的代码能运行是幸运的吗? 尝试在原始代码中添加一些额外的记录成员并观察它失败,然后你应该明白为什么我的方法有效。

以上是关于Delphi,如何在 TList 中释放记录的主要内容,如果未能解决你的问题,请参考以下文章

delphi TList中如何对一个double或者single类型的数据从小到大排序?

数组属性、TList、TStringList 或 TCollection 等 (Delphi Win32)

delphi 线程教学第六节:TList与泛型

【delphi线程】 如何自动释放?

如何释放接口对象 (Delphi 7)

求教在delphi中,如何把两个exe做为res加入到另一个exe中,并在运行时释放两个exe