这个无效指针在哪里?

3
我有以下代码(在释放之前,我已检查了objCur不是Nil):
try
  objCur.Free;
Except on E:Exception do
  begin
    OutputDebugString(PChar('Exception '+E.Message));
    Assert(False);
  end;
end;

它报告了以下异常信息:

无效的指针操作。objCur 是 TXX_TEA 类型。
objCur: TXX_TEA;

在 TXX_TEA.Destroy 中,我有以下代码:

destructor TXX_TEA.Destroy;
begin
  OutputDebugString(PChar('Inside Destroy'));
  ...
  inherited;
  OutputDebugString(PChar('End of Destroy'));
end;

在DebugView中,我看到以下消息:

销毁内部
...
销毁结束

异常:无效指针操作

我知道objCur.Free调用了TXX_TEA.Destroy,但是它似乎没有出现错误。那么我应该在哪里跟踪这个无效的指针操作呢?

3
仅仅检查objCur是否为非nil是不够的。事实上,如果它确实是nil,你根本不会有任何问题。你需要检查它是否引用了一个有效的对象,这实际上是无法在程序中自动检查的。你需要通过分析程序并确保没有任何导致变量具有无效引用的bug来进行检查。 - Rob Kennedy
实际上我想知道当objCur.Free时,它是否会首先调用TXX_TEA.Destroy,然后释放自身的属性?因为在TXX_TEA中,有一个属性Allos,Allos[i].xx = self.xx;并且在TXX_TEA.Destroy中,调用了Allos.Free。所以我怀疑当objCur释放其xx属性时,它是否已经被释放了。 - spspli
3个回答

9
一个无效的指针操作是指内存管理器试图释放不属于它的内存。
在最外层析构函数返回给调用者之前会释放对象的内存。在这种情况下,调用者是 TObject.Free。调用 inherited 不会导致对象的内存被释放,因为编译器知道它不是最外层的调用。
显然,您正在释放一个不存在的对象,但是这个假定对象的内存内容看起来足够有效,以至于在清理对象字段的析构函数中的代码没有崩溃。只有当析构函数运行完成并且要释放对象时,内存管理器才会检测到地址不引用当前分配的任何东西。

6
当你试图释放某个东西时出现“无效的指针操作”几乎总是意味着它已经被释放了。如果您想找到原因,最简单的方法是从SourceForge获取FastMM的完整版本。阅读文档,它将向您展示如何将其添加到您的项目中以及如何打开FullDebugMode。打开FullDebugMode后,当您尝试释放已经被释放的东西时,它将通过对话框中断程序,并为您提供对象第一次被释放时的堆栈跟踪等信息。这应该有助于您找到错误的原因。

3
很可能您仍然有对它的引用,某些代码使用该引用在释放后访问对象。这可以是一个对象引用或接口引用(非nil的接口引用将在作用域结束时调用_Release)。
我们需要查看更多代码才能真正找出原因。

那么有没有一种通用的方法来找出对象引用或接口引用的位置呢? - spspli
1
使用RTL和VCL调试单元进行调试将帮助您找出实际发生的情况,可能还会找出问题出在哪里。在异常被引发之前,也要查看堆栈跟踪。 - Rudy Velthuis
如果你从IDE调试选项中调用“在语言异常时中断”,你的调用堆栈仍将完整地保留在调试器中,直到实际抛出异常。这通常会揭示神秘异常的潜在来源。因此,在异常抛出之前查看堆栈跟踪可以得到+1分。 - Vector

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