Delphi中使用单个函数来释放内存和设置为nil的方法

8

我有很多内存分配和同样数量的FreeMem调用。但我没有在调用freemem之前检查指针是否为空,也没有在释放后设置指针为空。

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

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: Pointer)。虽然我不能使用 var,但我也不能这样做,因为编译器会抱怨类型必须是完全相同的类型(Pointer)。我传递的指针可能是任何类型的指针(PChar,常规指针等)。
我该怎么办才能解决这个问题?有更好的解决方案吗?

这里有一个名为 FreeAndNil 的函数。http://www.delphibasics.co.uk/RTL.asp?Name=FreeAndNil - Andrey
@Andrey FreeAndNil仅适用于对象。我正在处理手动分配给指针的内存。不过还是感谢您的建议。 - Daisetsu
4个回答

14
为了能够向该函数传递任意指针值,您需要遵循与FreeAndNil相同的模型并传递未命名参数。否则,编译器会正确地抱怨实际参数类型和形式参数类型不一致。在调用FreeMem时将未命名参数强制转换为Pointer。
您在该函数中做了一些无意义的事情。
首先是释放nil指针始终是安全的,因此在调用FreeMem之前没有必要检查该指针是否为nil。需要担心的是释放非nil指针,但是没有函数可以保护您免受此类问题的影响。
其次,多年来,FreeMem的大小参数一直被忽略。过去,如果您提供了该参数,它需要与传递给GetMem的大小匹配,但现在,FreeMem完全忽略了该参数-编译器甚至不将该参数传递给函数。
考虑到以上所有内容,您的函数简化为:
procedure FreeMemAndNil(var P);
var
  Tmp: Pointer;
begin
  Tmp := Pointer(P);
  Pointer(P) := nil;
  FreeMem(Tmp);
end;

要小心不要在任何不是使用GetMem分配的指针上意外调用该函数。如果您使用的是类型参数,则编译器无法像可能捕获的那样为您捕获它。如果您尝试释放未使用GetMem分配的内容,则很可能会收到EInvalidPointer异常,但是您传递的变量之后仍将为nil。这与FreeAndNil的工作方式相同。


我仍在使用相当旧的编译器Delphi 6。如果我释放了一个nil指针,我会得到一个错误,所以我确实需要先进行检查。即使我在使用非常老的编译器,你确定我不需要FreeMem的第二个参数吗? - Daisetsu
1
你误诊了问题。释放空指针一直是安全的。你的问题在别处。 - Rob Kennedy
@Daisetsu - 我刚在D6上尝试了这个方法,并且使用空指针正常工作。P := nil; FreeMem(P);没有报错。虽然未分配的指针可能指向垃圾。 - Gerry Coll
谢谢Rob,我想我错了。很抱歉没有选择你的答案作为“正确”的答案,我希望现在能改变我的选择。感谢你的帮助。 - Daisetsu
1
我对Stack Overflow的理解是,您可以随时更改您的选择。 - Rob Kennedy

9

SysUtils中有一个名为FreeAndNil的过程,可用于释放对象。它使用不带类型的var参数,并将其强制转换为TObject,你需要确保不要传递非TObject的参数。如果需要,你也可以在这里做类似的操作。但请小心:这样做没有类型安全性。


谢谢您的快速回复,我希望有一种方法可以表示多个答案都是正确的。 - Daisetsu
不,您必须只选择一个,但是您可以在有限的时间范围内更改。 - Uwe Raabe

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;

--jeroen

PS:Rob Kennedy在非类型化var参数上写了一个不错的答案,其中包含了他在网上的非类型化参数页面链接。

PS2:参考:Kylix版SysUtils.pas在线查看,那里的FreeAndNil与Delphi中的完全相同。


1
当然,如果你在GPL下使用Kylix代码,并且没有Kylix许可证,那么你整个应用程序现在就是GPL了。 - Marco van de Voort
1
上面展示的代码并不是“FreeAndNil”。但无论如何,我都不会使用这个代码;我宁愿使用Rob Kennedy的代码。 - Andreas Rejbrand
@Andreas:确实不是;Rob Kennedy的源代码没有像Daisetu所问的那样有一个size参数,并且使用了稍微不同的顺序进行清空和释放。 - Jeroen Wiert Pluimers
@Marco:那个Kylix链接仅供参考,我没有用它来修改来自Daisetu的代码(是的,我有Kylix许可证)。 - Jeroen Wiert Pluimers
我知道你这样做(或作为供应商极有可能),只是想警告公众不要在公共Kylix代码上过于自由。最好从Free Pascal获取,其许可证更自由。对于这样的基本内容,它将工作得很好。 - Marco van de Voort

4
我经常使用ReallocMem进行指针/内存操作。
调用
ReallocMem(P,0)

将指针设置为Nil。

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


+1;这对我来说是新的(似乎25年的Turbo Pascal经验还有很多学习旧事情的空间<g>)。 - Jeroen Wiert Pluimers

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