8

我有很多内存分配和相同数量的 FreeMem 调用。我没有的是在调用 freemem 之前检查指针是否为 nil,以及在释放后将指针设置为 nil 的一行。

我试图创建一个函数来做到这一点

procedure FreeMemAndNil(p: Pointer; size: Integer = -1);
begin
  if p <> nil then
  begin
    if size > -1 then
      FreeMem(p, size)
    else
      FreeMem(p);
    p := nil;
  end;
end;

但是有一个问题。它不能将原始指针设置为 nil,因为参数不是变量(var p:指针)。但我不能使用 var 因为如果我这样做编译器会抱怨类型必须是完全相同的类型(指针)。我传递的指针可以是指向任何类型(PChar、常规指针等)的指针。

我能做些什么来解决这个问题?有更好的解决方案吗?

4

4 回答 4

14

为了能够将任意指针值传递给该函数,您需要遵循与 FreeAndNil 相同的模型并传入一个无类型参数。否则,编译器会正确地抱怨实际参数类型和形式参数类型不相同。当您在其上调用 FreeMem 时,将无类型参数类型转换为 Pointer。

您在该功能中做了一些毫无意义的事情。

首先是释放一个 nil 指针总是安全的,所以没有理由在调用 FreeMem 之前检查它。它释放了您需要担心的非零指针,但没有任何功能可以保护您免受这种情况的影响。

接下来,FreeMem 的 size 参数多年来一直被忽略。过去,如果您提供该参数,它需要与传递给 GetMem 的大小相匹配,但现在,FreeMem 完全忽略了该参数——编译器甚至不将该参数传递给函数。

考虑到以上所有因素,您的功能归结为:

procedure FreeMemAndNil(var P);
var
  Tmp: Pointer;
begin
  Tmp := Pointer(P);
  Pointer(P) := nil;
  FreeMem(Tmp);
end;

注意不要在不是用 GetMem 分配的指针的任何东西上意外调用该函数。如果您使用类型化参数,编译器不会像它那样为您捕获它。如果你试图释放没有用 GetMem 分配的东西,你可能会得到一个 EInvalidPointer 异常,但是你传入的变量之后仍然是 nil。这与 FreeAndNil 的工作方式相同。

于 2010-08-11T18:09:51.463 回答
9

在 SysUtils 中有一个名为 FreeAndNil 的过程可以对对象执行此操作。它通过使用它强制转换为 TObject 的无类型var参数来实现,并且由您来确保您不会将非 TObject 的东西传递给它。如果需要,您可以在这里做类似的事情。小心点;如果你这样做,就没有类型安全。

于 2010-08-11T17:30:56.550 回答
6

就像Mason Wheeler所说,您应该使用与SysUtils单元中的FreeAndNil相同的技巧来处理对象引用。
因此,我修改了您的代码,对其进行了单元测试,并且效果很好:

procedure FreeMemAndNil(var ptr; size: Integer = -1);
var
  p: Pointer;
begin
  p := Pointer(ptr);
  if p <> nil then
  begin
    if size > -1 then
      FreeMem(p, size)
    else
      FreeMem(p);
    Pointer(ptr) := nil;
  end;
end;

——杰伦

PS:Rob Kennedy 写了一个关于无类型 var 参数的好答案,其中有一个链接到他在互联网上的无类型参数页面。

PS2:供参考: SysUtils.pas的 Kylix 版本已上线,FreeAndNil 与 Delphi 中的相同。

于 2010-08-11T17:58:42.190 回答
4

我倾向于使用 ReallocMem 进行指针/内存操作。

打电话

ReallocMem(P,0)

将指针设置为 Nil。

关于使用它你需要知道的一件事,P 需要在传递给 ReallocMem 之前进行初始化。

于 2010-08-12T17:13:51.577 回答