Delphi 什么';在FreeAndNil之后使用对象时应该发生什么?

Delphi 什么';在FreeAndNil之后使用对象时应该发生什么?,delphi,Delphi,在我的Delphi7中,这段代码 var MStr: TMemoryStream; ... FreeAndNil(MStr); MStr.Size:=0; 在模块“Project1.exe”的地址0041D6D1处生成AV:Access冲突。读取地址00000000。 但有人坚持认为,无论发生什么情况,它都不应该引发任何例外。他还说,他的Delphi5确实没有提出任何例外。他称之为“陈旧的指针错误”。 换句话说,他说FreeAndNil不能用作调试器来检测释放对象或使用释放对象的双重尝试 有

在我的Delphi7中,这段代码

var MStr: TMemoryStream;
...
FreeAndNil(MStr);
MStr.Size:=0; 
在模块“Project1.exe”的地址0041D6D1处生成AV:Access冲突。读取地址00000000。 但有人坚持认为,无论发生什么情况,它都不应该引发任何例外。他还说,他的Delphi5确实没有提出任何例外。他称之为“陈旧的指针错误”。 换句话说,他说FreeAndNil不能用作调试器来检测释放对象或使用释放对象的双重尝试

有谁能启发我吗?这是否会引发错误(总是/随机),或者程序应该毫无问题地运行此错误

谢谢


我问这个问题是因为我相信我的程序中有一个“双重自由对象”或“自由再访问”错误。释放对象后,如何用零填充分配给对象的内存?我想用这种方法通过获取和AV来检测bug的位置。
最初,我希望如果我将对象设置为FreeAndNil,在尝试重新访问它时,我将始终获得AV

据我所见,此代码应始终导致错误。FreeAndNil显式地将传递的值设置为Nil(也称为0),因此,在尝试取消引用对象时,绝对会遇到访问冲突。

使用空引用的方法或属性总是错误的,即使它有时似乎有效

FreeAndNil
确实不能用于检测双重空闲。对已经为零的变量调用
FreeAndNil
是安全的。因为它是安全的,它不会帮你发现任何东西

这不是一个陈旧的指针错误。这是一个空引用错误。过时指针错误是指您已释放对象,但未清除引用该对象的所有变量。然后变量仍然保存对象的旧地址。这些很难被发现。您可能会遇到这样的错误:

MStr := TMemoryStream.Create;
MStr.Free;
MStr.Size := 0;
MStr := TMemoryStream.Create;
OtherStr := MStr;
FreeAndNil(MStr);
OtherStr.Size := 0;
你也可以得到这样一个:

MStr := TMemoryStream.Create;
MStr.Free;
MStr.Size := 0;
MStr := TMemoryStream.Create;
OtherStr := MStr;
FreeAndNil(MStr);
OtherStr.Size := 0;
释放引用的对象后使用
MStr.Size
是一个错误,应该会引发异常。它是否引发异常取决于实现。也许会,也许不会。但这不是随机的

如果您正在搜索双重免费bug,您可以使用FastMM提供的调试助手,正如其他人所建议的那样。它的工作原理是不会将内存释放回操作系统,甚至不会释放回Delphi的内部可用内存池。相反,它会将已知的坏数据写入对象的内存空间,因此当您看到这些值时,就知道您正在读取已释放的数据。它还修改对象的VMT,以便下次在该对象引用上调用虚方法时,您将得到一个可预测的异常,它甚至会告诉您您尝试使用的是哪个假定已释放的对象。当您再次尝试释放对象时,它不仅可以告诉您已经释放了对象,还可以告诉您第一次释放对象的位置(使用堆栈跟踪),以及分配对象的位置。它还收集这些信息以报告内存泄漏,即释放对象的次数少于一次而不是更多

您还可以使用以下习惯来避免未来代码出现问题:

  • 减少使用全局变量。在整个程序中,任何代码都可以修改全局变量,这迫使您在使用它时产生疑问,“这个变量的值是否仍然有效,或者其他代码是否已经释放了它?”当您限制变量的范围时,在查找变量没有期望值的原因时,减少程序中必须考虑的代码量。
  • 明确谁拥有一个对象。当有两段代码可以访问同一个对象时,您需要知道哪些代码段拥有该对象。它们可能都有一个不同的变量来引用对象,但仍然只有一个对象。如果一段代码对其变量调用了
    FreeAndNil
    ,那么另一段代码的变量仍然保持不变。如果另一个代码认为它拥有这个对象,那么你就有麻烦了。(所有者的概念不一定与
    TComponent.owner
    属性相关联。不需要拥有它的对象;它可以是程序的通用子系统。)
  • 不要保留对您不拥有的对象的持久引用。如果不保留对对象的长期引用,则不必担心这些引用是否仍然有效。唯一的持久引用应该在拥有该对象的代码中。需要使用该对象的任何其他代码都应该接收一个引用作为输入参数,使用该对象,然后在返回其结果时放弃该引用

如果将指针设置为nil,则不能再使用它。但是如果你有另一个指向同一个对象的指针,你可以使用它而不需要AV,因为这个指针仍然指向对象地址而不是零

此外,释放对象不会清除该对象使用的内存。它只是将其标记为未使用。这就是你想要AV的原因。如果释放的内存分配给另一个对象,您将得到一个AV,因为它不再包含看起来有效的数据

FastMM4有一些设置,您可以在调试时使用这些设置来检测这种情况。从FsatMM4Options.inc:

{设置以下选项以对所有内存块执行扩展检查。全部 区块内填充有一个收割台和一个拖车,用于验证 堆的完整性。释放的块也会被清除,以确保 释放后无法重新使用。此选项会减慢内存操作 并且只应用于调试 覆盖内存或重用释放的指针。设置此选项 自动启用CheckHeapForCorruption并禁用ASMVersion。 非常重要:如果你