如何在Delphi中正确释放包含各种类型的记录?

Rai*_*ith 15 delphi records

type
  TSomeRecord = Record
    field1: integer;
    field2: string;
    field3: boolean;
  End;
var
  SomeRecord: TSomeRecord;
  SomeRecAr: array of TSomeRecord;
Run Code Online (Sandbox Code Playgroud)

这是我所拥有的最基本的例子,因为我想重复使用SomeRecord(某些字段保持为空,没有释放所有字段将在重复使用时被转移SomeRecord,这显然是不受欢迎的)我正在寻找一种方法一次释放所有字段.我已经开始string[255]使用ZeroMemory(),这很好,直到它开始泄漏内存,这是因为我切换到string.我仍然缺乏了解原因的知识,但似乎与动态有关.我也在使用动态数组,因此我认为尝试ZeroMemory()任何动态都会导致泄漏.有一天浪费了这一点.我想,我通过使用解决了这个Finalize()SomeRecordSomeRecAr之前ZeroMemory(),但我不知道这是正确的做法,或只是我太傻了.

所以问题是:如何一次性解放所有东西?是否存在一些我不知道的单一程序?

换句话说,或者我会建议如何以不同的方式实现这些记录,所以我不需要做出复杂的尝试来释放东西.我已经研究过创建记录New()然后去掉它Dispose(),但我不知道当调用之后的变量Dispose()是未定义的而不是nil 时它意味着什么.另外,我不知道某个type(SomeRecord: TSomeRecord)的变量与指向type()的变量之间的区别是什么SomeRecord: ^TSomeRecord.我现在正在调查上述问题,除非有人能够快速解释,否则可能需要一些时间.

Dav*_*nan 27

假设您有一个支持在记录上实现方法的Delphi版本,您可以清除这样的记录:

type
  TSomeRecord = record
    field1: integer;
    field2: string;
    field3: boolean;
    procedure Clear;
  end;

procedure TSomeRecord.Clear;
begin
  Self := Default(TSomeRecord);
end;
Run Code Online (Sandbox Code Playgroud)

如果您的编译器不支持,Default那么您可以像这样完全相同:

procedure TSomeRecord.Clear;
const
  Default: TSomeRecord=();
begin
  Self := Default;
end;
Run Code Online (Sandbox Code Playgroud)

您可能更愿意避免在方法中改变值类型.在这种情况下,创建一个返回空记录值的函数,并将其与赋值运算符一起使用:

type
  TSomeRecord = record
    // fields go here
    class function Empty: TSomeRecord; static;
  end;

class function TSomeRecord.Empty: TSomeRecord;
begin
  Result := Default(TSomeRecord);
end;

....

Value := TSomeRecord.Empty;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我找不到任何文档参考Default(TypeIdentifier).有谁知道它在哪里可以找到?


至于问题的第二部分,我认为没有理由不继续使用记录,并使用动态数组分配它们.试图自己管理生命周期更容易出错.

  • 同意。没有什么不好的感觉,我会保持现在的样子。 (2认同)
  • 我正要问同样的问题,因为快速查看帮助并没有产生任何关于`Default()`的信息.我确实有XE2,所以它绝对支持记录中的方法.`Default()`但是,我不知道,我需要尝试一下.我确实喜欢你可以做`EmptySomeRecord:TSomeRecord =()`,我将来肯定会在某个地方使用它. (2认同)

Arn*_*hez 7

不要让思想过于复杂!

分配"默认" record只是CPU功耗和内存的损失.

当a在a record中声明时TClass,它被填充为零,因此被初始化.当它在堆栈上分配时,只初始化引用计数变量:其他类型的变量(如整数或双精度或布尔值或枚举)处于随机状态(可能非零).当它将在堆上分配时,getmem将不会初始化任何内容,allocmem将所有内容填充为零,并将new仅初始化引用计数的成员(如在堆栈初始化时):在所有情况下,您应该使用其中任何一个dispose,要么finalize+freemem释放一个堆分配record.

所以关于你的确切问题,你自己的假设是正确的:在使用后重置记录内容,永远不要使用" fillchar"(或" zeromemory")而不使用前一个" finalize".这是正确和最快的方式:

Finalize(aRecord);
FillChar(aRecord,sizeof(aRecord),0);
Run Code Online (Sandbox Code Playgroud)

再次,它将比分配默认记录更快.并且在所有情况下,如果您使用Finalize,甚至多次,它将不会泄漏任何内存 - 100%退款保修!

编辑:在查看生成的代码之后aRecord := default(TRecordType),代码得到了很好的优化:它实际上是Finalize一堆stosd模拟的代码FillChar.因此,即使语法是copy/assignement(:=),它也不会实现为复制/赋值.我的错误在这里.

但我仍然不喜欢:=必须使用的事实,Embarcadero应该更好地使用recordaRecord.Clear语法一样的方法,就像DelphiWebScript的动态数组一样.实际上,这种:=语法与C#使用的语法完全相同.听起来如果Embacardero在任何地方都模仿C#语法,却没有发现这很奇怪.如果德尔福只是一个追随者,而不是实施者认为"它的方式"是什么意思呢?人们总是更喜欢原始的C#到它的祖先(Delphi有同样的父亲).

  • 你的反对者显然改变了主意,但在这里应该得到反对票。首先,性能可能不是 OP 最重要的因素。但更重要的是,你在这里的断言是不正确的。`aRecord := Default(TSomeRecord)` 是有效的。 (2认同)
  • @DavidHeffernan 我刚刚检查了 Self := default(record) 生成的代码。它使用 stosd 完成 Finalize + inline fillchar。它经过优化(直到您不使用“rep stosd”)。所以你是对的,这是关于速度的好代码。如果只有记录副本(更多使用)已经得到了如此多的优化。 (2认同)

NGL*_*GLN 5

我想到的最简单的解决方案是:

const
  EmptySomeRecord: TSomeRecord = ();
begin
  SomeRecord := EmptySomeRecord;
Run Code Online (Sandbox Code Playgroud)

但要解决问题的所有其余部分,请采用以下定义:

type
  PSomeRecord = ^TSomeRecord;
  TSomeRecord = record
    Field1: Integer;
    Field2: String;
    Field3: Boolean;
  end;
  TSomeRecords = array of TSomeRecord;
  PSomeRecordList = ^TSomeRecordList;
  TSomeRecordList = array[0..MaxListSize] of TSomeRecord;    
const
  EmptySomeRecord: TSomeRecord = ();
  Count = 10;    
var
  SomeRecord: TSomeRecord;
  SomeRecords: TSomeRecords;
  I: Integer;
  P: PSomeRecord;
  List: PSomeRecordList;

procedure ClearSomeRecord(var ASomeRecord: TSomeRecord);
begin
  ASomeRecord.Field1 := 0;
  ASomeRecord.Field2 := '';
  ASomeRecord.Field3 := False;
end;

function NewSomeRecord: PSomeRecord;
begin
  New(Result);
  Result^.Field1 := 0;
  Result^.Field2 := '';
  Result^.Field3 := False;
end;
Run Code Online (Sandbox Code Playgroud)

然后是一些关于如何操作它们的例子:

begin
  // Clearing a typed variable (1):
  SomeRecord := EmptySomeRecord;

  // Clearing a typed variable (2):
  ClearSomeRecord(SomeRecord);

  // Initializing and clearing a typed array variabele:
  SetLength(SomeRecords, Count);

  // Creating a pointer variable:
  New(P);

  // Clearing a pointer variable:
  P^.Field1 := 0;
  P^.Field2 := '';
  P^.Field3 := False;

  // Creating and clearing a pointer variable:
  P := NewSomeRecord;

  // Releasing a pointer variable:
  Dispose(P);

  // Creating a pointer array variable:
  ReallocMem(List, Count * SizeOf(TSomeRecord));

  // Clearing a pointer array variable:
  for I := 0 to Count - 1 do
  begin
    Pointer(List^[I].Field2) := nil;
    List^[I].Field1 := 0;
    List^[I].Field2 := '';
    List^[I].Field3 := False;
  end;

  // Releasing a pointer array variable:
  Finalize(List^[0], Count);
Run Code Online (Sandbox Code Playgroud)

根据需要选择和/或组合.

  • 更常见的常量定义是`EmptySomeRecord:TSomeRecord =();`并让编译器填入零. (4认同)