22
type
  TSomeRecord = Record
    field1: integer;
    field2: string;
    field3: boolean;
  End;
var
  SomeRecord: TSomeRecord;
  SomeRecAr: array of TSomeRecord;

这是我所拥有的最基本的示例,因为我想重用SomeRecord(某些字段保持为空,而不释放所有内容,当我重用时,某些字段将被保留SomeRecord,这显然是不希望的)我正在寻找一种方法来一次释放所有字段。我开始string[255]使用. 我仍然缺乏了解原因的知识,但这似乎与它是动态的有关。我也在使用动态数组,所以我认为尝试任何动态都会导致泄漏。花了一天时间才弄清楚这一点。我想我通过使用on或before解决了这个问题ZeroMemory()stringZeroMemory()Finalize()SomeRecordSomeRecArZeroMemory(),但我不确定这是正确的方法还是只是我很愚蠢。

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

换一种说法,或者我愿意接受有关如何以不同方式实施这些记录的建议,因此我不需要在释放内容时进行复杂的尝试。我已经研究过使用它创建记录New()然后将其删除Dispose(),但我不知道调用后的变量Dispose()未定义而不是 nil 时意味着什么。另外,我不知道某个类型的变量(SomeRecord: TSomeRecord)与指向某个类型的变量( )之间有什么区别SomeRecord: ^TSomeRecord。我目前正在研究上述问题,除非有人能快速解释,否则可能需要一些时间。

4

4 回答 4

33

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

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

procedure TSomeRecord.Clear;
begin
  Self := Default(TSomeRecord);
end;

如果您的编译器不支持Default,那么您可以像这样简单地做同样的事情:

procedure TSomeRecord.Clear;
const
  Default: TSomeRecord=();
begin
  Self := Default;
end;

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

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;

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


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

于 2012-06-16T18:57:21.873 回答
8

不要让想法过于复杂!

分配“默认值”record只是 CPU 功率和内存的损失。

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

所以关于你的确切问题,你自己的假设是正确的:在使用后重置记录内容,永远不要在没有前一个“”的情况下使用“ fillchar”(或“ zeromemory”)finalize。这是正确和最快的方法:

Finalize(aRecord);
FillChar(aRecord,sizeof(aRecord),0);

再一次,它比分配默认记录要快。在任何情况下,如果您使用Finalize,甚至多次使用,它都不会泄漏任何内存 - 100% 退款保修!

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

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

于 2012-06-16T20:41:59.407 回答
6

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

const
  EmptySomeRecord: TSomeRecord = ();
begin
  SomeRecord := EmptySomeRecord;

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

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;

然后这里有一些关于如何操作它们的多个示例:

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);

随意选择和/或组合。

于 2012-06-16T18:13:20.157 回答
1

SomeRecord: TSomeRecord,将SomeRecord是类型的实例/变量TSomeRecord。使用SomeRecord: ^TSomeRecord,SomeRecord将是指向类型的实例或变量的指针TSomeRecord。在最后一种情况下,SomeRecord将是一个类型化的指针。如果您的应用程序在例程之间传输大量数据或与外部 API 交互,建议使用类型指针。

new()并且dispose()仅与类型化指针一起使用。使用类型化的指针,编译器无法控制/了解您的应用程序正在使用这种 vars 的内存。释放类型指针使用的内存取决于您。

另一方面,当您使用普通变量时,根据使用和声明,编译器将在认为不再需要它们时释放它们使用的内存。例如:

function SomeStaff();
var
    NativeVariable: TSomeRecord;
    TypedPointer: ^TSomeRecord;
begin
    NaviveVariable.Field1 := 'Hello World';

    // With typed pointers, we need to manually
    // create the variable before we can use it.
    new(TypedPointer);
    TypedPointer^.Field1 := 'Hello Word';

    // Do your stuff here ...

    // ... at end, we need to manually "free"
    // the typed pointer variable. Field1 within
    // TSomerecord is also released
    Dispose(TypedPointer);

    // You don't need to do the above for NativeVariable
    // as the compiler will free it after this function
    // ends. This apply also for native arrays of TSomeRecord.
end;

在上面的例子中,变量NativeVariable只在SomeStaff函数内部使用,所以编译器会在函数结束时自动释放它。这适用于几乎大多数本机变量,包括数组和记录“字段”。对象的处理方式不同,但这是另一篇文章。

于 2012-06-16T19:27:10.577 回答