获取内存 x 重新分配内存

5

System.GetMem和System.ReallocMem有什么区别?

在Delphi 2009中,ReallocMem的帮助文档与GetMem完全相同。那么System.FreeMem和System.Dispose呢?

在处理数组时应该使用哪种方法?

type
  PMemberDataList = ^TMemberDataList;
  TMemberDataList = array[0..MaxClassMembers -1] of PMemberData;

var
  FItems: PMemberDataList;

begin
  GetMem(FItems, Value * SizeOf(Pointer));
  FreeMem(FItems);
end;

或者

begin
  ReallocMem(FItems, Value * SizeOf(Pointer));
  Dispose(FItems);
end;

解决方案

在听取了其他人的建议后,我将FItems声明为记录类型而不是指向记录的指针,将TMemberDataList声明为动态数组,使用SetLength来(de)分配数组,使用New/Dispose来处理数据。

type
  PMemberDataList = ^TMemberDataList;
  TMemberDataList = array of PMemberData;
var
  Items: TMemberDataList;
  Item: PMemberData;

// Add
begin
  Setlength(Items, 1);
  New(Item);
  Items[0]:= Item
end;

// Remove
begin
  Dispose(Items[0]);
  Setlength(Items, 0);
end;

根据您更新后的使用情况,看起来数组“拥有”成员数据。您可以将其改为TMemberData数组而不是PMemberData,这样可以进一步简化代码。 - Rob Kennedy
2个回答

20

GetMem分配内存,FreeMem释放/释放内存,ReallocMem可能执行其中一个、两个或者全部。实际上,在正确使用时,ReAllocMem是唯一需要的内存管理API。如果您从零指针开始,并调用大小>0的ReAllocMem,则它的行为类似于GetMem。如果您使用size = 0调用ReAllocMem,则它的行为类似于FreeMem。唯一实际“重新分配”内存的时间是当指针非nil且大小>0时。

New和Dispose旨在与类型指针一起使用,或者对于你们“老派”的人,与较旧的Turbo Pascal对象模型(旧的“object”语法)一起使用。New和Dispose还将确保任何引用受控类型的类型指针都将正确地初始化该类型。例如,在给定以下内容的情况下:

type
  PMyRec = ^TMyRec;
  TMyRec = record
    Name: string;
    Value: Variant;
  end;

var
  Rec: PMyRec;
begin
  New(Rec);
  try
    Rec.Name := 'TestValue';
    Rec.Value := 100;
    ...
  finally
    Dispose(Rec);
  end;
end;

New和Dispose会确保记录的Name和Value字段被正确地初始化和终止或清理。在上述情况下,New和Dispose等效于:

GetMem(Rec, SizeOf(Rec^));
Initialize(Rec);
...
Finalize(Rec);
FreeMem(Rec);

对于你提供的例子,Gamecat是正确的,你最好使用动态数组,因为它们受到编译器更好的管理,并且它们还具有自己的固有长度。 对于你的例子,你需要单独跟踪数组中的项目数量,这样无论你在哪里传递数组,你都必须同时传递当前分配的长度。通过使用动态数组,所有信息都被整齐地打包在一起。这将允许您遍历数组,而不考虑当前长度,只需执行以下操作之一:

var
  Member: TMemberData;
  Items: array of TMemberData;
  ...
begin
  SetLength(Items, Value);
  for Member in Items do  // iterate over each element in the array
  ...
  for Low(Items) to High(Items) do // same as above only using std functions
  ...
end;

最后,你可能想使用动态数组的另一个原因是,如果TMemberData包含字符串、变体、接口或其他“托管”类型,它们将被正确地初始化和完成,而无需手动执行。


5
@Allen:你的回答应该被添加到Delphi帮助文档中 :) - Cesar Romero
1
同意。 (不过Delphi帮助还需要添加很多东西!):P - Mason Wheeler
Rec需要在Initialize和Finalize调用中被取消引用。否则它们什么也不做。(至少编译器会发出警告。) - Rob Kennedy

3

GetMem函数分配一块内存,ReallocMem函数重新分配一块内存。

但是最好使用动态数组:

var
  FItems : array of TMemberDataList;

begin
  SetLength(FItems, Value);
end;

这更符合Delphi的方式。

示例

你可以选择:

type
  TMemberDataList = array[0..MaxClassMembers -1] of TMemberData;

var
  FItems: TMemberDataList;
begin
  // Don't need to allocate FItems
end;

或者:

type
  TMemberDataList = array of TMemberData;

var
  FItems: TMemberDataList;
begin
  SetLength(FItems, MaxClassMembers);
end;

类变量是指针。因此,您不必像在TP中那样使用显式指针。您仍然可以使用记录或对象的指针,但没有理由这样做。


@Gamecat:针对这个问题,我需要将PMemberDataList作为变量传递给方法参数。代码: SetLength(FItems^[I]^.ClassItems^, Size); 会引发异常 [DCC Error] JazzIntrospector.pas(433): E2008 不兼容的类型 - Cesar Romero
不确定FItems是如何定义的,但你不应该需要那些显式指针。(我添加了一个例子) - Toon Krijthe
定义如同我问题中的代码。如果我尝试将TMemberDataList作为方法参数传递,编译器会报错。 - Cesar Romero

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