对已释放的对象的错误引用
有没有一种方法可以确保我们持有对对象的可用引用,即确保尚未释放该对象,而使该非nil引用悬而未决。
解决方案
不幸的是,除非精心编写正确的代码,否则无法100%保证指向任何内容的指针仍然有效。
如果我们将FastMM4用作内存管理器,则可以检查该类是否不是TFreeObject。
或者,在更标准的情况下,请使用例程,该例程通过检查VMT类来验证对象是否像它所说的那样。
已经有一段时间验证了此类ValidateObj函数(由Ray Lischner和Hallvard Vassbotn撰写:http://hallvards.blogspot.com/2004/06/hack-6checking-for-valid-object.html)
这是另一个:
function ValidateObj(Obj: TObject): Pointer; // see { Virtual method table entries } in System.pas begin Result := Obj; if Assigned(Result) then try if Pointer(PPointer(Obj)^) <> Pointer(Pointer(Cardinal(PPointer(Obj)^) + Cardinal(vmtSelfPtr))^) then // object not valid anymore Result := nil; except Result := nil; end; end;
更新:谨慎一点...上面的函数将确保结果为nil或者有效的non nil对象。如果内存管理器已经重新分配了先前释放的内存,则不能保证Obj仍然是我们想的。
不会。除非我们使用引用计数或者垃圾收集器之类的方法来确保没有任何对象在引用为零之前被释放。
如果使用接口,Delphi可以为我们做引用计数。当然,Delphi for .Net有一个垃圾收集器。
如前所述,我们可以使用Delphi的知识或者内存管理器的内部知识来检查有效的指针或者对象,但是它们并不是唯一可以为我们提供指针的对象。因此,即使使用这些方法,我们也无法涵盖所有指针。而且指针也有可能再次变为有效,但又被赋予其他人。因此,它不是我们要查找的指针。设计不应依赖它们。使用工具来检测我们所犯的任何参考错误。
标准,不...
这就是为什么VCL组件可以注册自己以通知对象破坏的原因,以便它们可以从组件的内部列表中删除引用或者仅重置其属性。
因此,如果要确保我们没有任何无效的引用,则有两个选择:
- 实现每个类都可以订阅的销毁通知处理程序。
- 修正代码,使引用不会散布在不同的对象上。例如,我们只能通过另一个对象的属性来提供对引用的访问。而不是将引用复制到私有字段,我们可以访问另一个对象的属性。
就像其他人说的那样,这不是确定的方法,但是如果我们很好地管理所有权,那么FreeAndNil例程将确保变量为nil(如果它没有指向任何内容)。
无论如何,检查引用是否有效通常不是一个好主意。如果引用无效,则程序将在使用无效引用的位置崩溃。否则,无效引用可能会生存更长的时间,并且调试会变得更加困难。
以下是一些有关为什么最好使无效引用崩溃的参考。 (他们谈论Win32中的指针,但是这些想法仍然有意义):
- IsBadXxxPtr应该真正称为CrashProgramRandomly
- 我应该检查功能的参数吗?
通过使用接口引用(而不是对象引用),可以避免这些无效的指针问题,因为代码中不再存在对Free的显式调用。