多谢小雨哥让我发现了那么好的帖子。
粗看了一下,darkiss 的 < 安全的指针 > ,以及 aimingoo 的评论,
觉得 aimingoo 的评论没有说到点子上,darkiss 对于异常的理解的确
是有问题,不过那不是主要问题。
强调一下,我只是粗略看了一看。darkiss 的“安全指针”并不安全,
他的 TSafePtr 不仅没有解决悬空指针的问题,而且还有几个 bug :)
darkiss 的源代码摘录如下:(添加了一行以修正一个 bug)
{**********************************************************************
SaftPtr
Automatically allocate memories and free them
Author
arkiss
Create Date :2001/10/17
**********************************************************************}
unit SafePtr;
interface
uses
classes, SysUtils ;
type
TObjRecord = class
private
FClass :TClass ;
FObject :TObject ;
public
constructor Create(SomeClass:TClass) ;
destructor Destroy ;override ;
published
property ptrClass :TClass read FClass ;
property ptrObject:TObject read FObject;
end ;
TSafePtr = class
private
FList:TList ;
public
constructor Create ;
overload ;
destructor Destroy ;override ;
function Create(SomeClass:TClass):TObject ;
overload ;
end ;
implementation
{ TObjRecord }
constructor TObjRecord.Create(SomeClass:TClass);
begin
if not Assigned(SomeClass) then
raise Exception.Create('TObjRecord.Create: Invalid Class!') ;
FClass := SomeClass ;
try
FObject := FClass.Create ;
except
on Exceptiondo
raise Exception.Create('TObjRecord.Create: Can not Create Object!');
end ;
end;
destructor TObjRecord.Destroy;
begin
if Assigned(FObject) then
with FObject as FClassdo
free ;
inherited;
end;
{ TSafePtr }
constructor TSafePtr.Create ;
begin
FList := FList.Create ;
end;
function TSafePtr.Create(SomeClass: TClass): TObject;
var temp :TObjRecord ;
begin
try
temp := TObjRecord.Create(SomeClass) ;
except
raise Exception.Create('TSaftPtr.Create: Error Create Ptr') ;
exit ;
// 这行不会被执行到,应略去,aimingoo 已经指出了
end ;
FList.Add(temp) ;
// 以下由 beta 添加一行:
Result := temp;
// 要不然这个函数怎么用?:)
end;
destructor TSafePtr.Destroy;
var temp :TObjRecord ;
begin
while Boolean(FList.Count)do
// 这一行的用法倒是挺有意思:)
begin
temp := FList.Last ;
with temp.FObject as temp.FClassdo
free ;
FList.Delete(FList.Count-1) ;
end ;
FList.Clear ;
inherited;
end;
end.
问题:
在 TSafePtr 创建的时候创建了一个 TObjRecord 对象,并加入了 FList,
但是在 TSafePtr 释放的时候却没有释放 FList 中的 TObjRecord 对象,
而是释放了 TObjRecord 对象中的 FObject,而这应该是 TObjRecord 释放
的时候干的事情。因此,按照 darkiss 的思路,TSafePtr 的析构函数应该
改写如下:
destructor TSafePtr.Destroy;
var temp :TObjRecord ;
begin
while Boolean(FList.Count)do
begin
temp := FList.Last ;
temp.free ;
// 修改成这个样子
FList.Delete(FList.Count-1) ;
end ;
FList.Clear ;
FList.Free;
// 还要记得释放他,犯了和我一样的错误:)
inherited;
end;
这仅仅是修正了 bug,但是并不能解决问题,因为这个方案本身就存在着缺
陷。看看 TObjRecord 的析构函数:
destructor TObjRecord.Destroy;
begin
if Assigned(FObject) then
with FObject as FClassdo
free ;
inherited;
end;
关键就出在 if Assigned(FObject) then
这一行上,试想要是用户已经该
对象释放,那么此时调用 FObject.Free 就会出现问题。
从根本上说,他这个方案就是没有考虑悬空指针的问题。
我认为,darkiss 该方法的提出本意可能是想实现对象的自动释放,即所谓的
“垃圾回收”(应该就是这么回事吧?[
]),却没有考虑到在你回收垃圾
之前,某垃圾就已经被“处理”了。
这时,我的自动清空实例指针的办法就派上用场了。就是说,对于某个类,先
用我的办法将其改装为可以自动清空实例指针的类,然后用 darkiss 的办法进
行“包装”使用。这样就可以从一定程度上实现较可靠的“垃圾回收”。不过
这个办法有一个很大的局限,就是只能可靠的回收经过“改装”的类的对象,
无法通用,而且它这个外壳负责回收,却没人来回收它,还是得手动释放:)
欢迎继续讨论。