Delphi 德尔福:为什么';t freeanndnil*真的*nil我的目标?

Delphi 德尔福:为什么';t freeanndnil*真的*nil我的目标?,delphi,memory-management,Delphi,Memory Management,我想把一个对象A传递给第二个对象B,让B做一些处理,最后释放A,以防不再需要它。下面给出了一个淡化的版本 program Project6; {$APPTYPE CONSOLE} uses SysUtils; type TMyObject = class(TObject) public FField1: string; FField2: string; end; TBigObject = class(TObject) public FMyObj

我想把一个对象A传递给第二个对象B,让B做一些处理,最后释放A,以防不再需要它。下面给出了一个淡化的版本

program Project6;
{$APPTYPE CONSOLE}
uses
  SysUtils;
type
  TMyObject = class(TObject)
  public
    FField1:  string;
    FField2:  string;
  end;
  TBigObject = class(TObject)
  public
    FMyObject:  TMyObject;
    procedure Bind(var MyObject:  TMyObject);
    procedure Free();
  end;
procedure TBigObject.Bind(var MyObject: TMyObject);
begin
  FMyObject := MyObject;
end;
procedure TBigObject.Free;
begin
  FreeAndNil(FMyObject);
  Destroy();
end;
var
  MyObject:   TMyObject;
  BigObject:  TBigObject;
begin
  try
    MyObject := TMyObject.Create();
    BigObject := TBigObject.Create();
    BigObject.Bind(MyObject);
    BigObject.Free();
    if (Assigned(MyObject)) then begin
      WriteLn('Set MyObject free!');
      MyObject.Free();
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
(不要介意糟糕的设计。)现在,我不明白的是为什么FreeAndNil实际上会释放MyObject,而
Assigned(MyObject)
的计算结果为true(在
MyObject.free()给出一个AV值


有人能帮我指点一下吗?

MyObject
是与字段
FMyObject
不同的变量。而您只对字段
FMyObject
执行
nil
操作

FreeAndNil
释放到指向的对象,并
nil
s传递的变量。它不会神奇地发现和
nil
指向您释放的对象的所有其他变量

FreeAndNil(FMyObject)执行与以下相同的操作:

object(FMyObject).Free();
FMyObject=nil;
(从技术上讲,这并不完全正确,由于未类型化的
var
参数,强制转换为对象是一个重新解释强制转换,但这与此处无关)

这显然只会修改
FMyObject
,而不会修改
mybject


哦,我刚刚注意到你隐藏了原来的
Free
方法?那太疯狂了
FreeAndNil
仍使用原始的
Free
。在您的示例中,这不会影响您,因为您对静态类型为
TBigObject
的变量调用
Free
,而不是
FreeAndNil
。但这是一张灾难收据


相反,您应该重写析构函数
Destroy

您有两个对象引用副本,但只将其中一个设置为nil。您的代码与此等效:

i := 1;
j := i;
i := 0;
Writeln(j);//outputs 1
我在本例中使用整数,因为我相信您熟悉它们的工作原理。对象引用实际上只是指针,其行为方式完全相同

根据对象引用强制转换示例使其看起来如下所示:

obj1 := TObject.Create;
obj2 := obj1;
obj1.Free;//these two lines are
obj1 := nil;//equivalent to FreeAndNil
//but obj2 still refers to the destroyed object


旁白:您不应该直接调用Destroy,也不应该声明一个名为Free的方法。相反,重写Destroy并调用TObject中定义的静态Free,或者实际上是FreeAndNil。

原因很简单,一个引用为零,而另一个引用为零。考虑这个例子:

var
  Obj1, Obj2: TObject;
begin
  Obj1 := TObject.Create;
  Obj2 := Obj1;
  FreeAndNil(Obj1);
  // Obj1 is released and nil, Obj2 is non-nil but now points to undefined memory
  // ie. accessing it will cause access violations
end;

您的代码中有一些特性

首先,您不应该自由重写,应该重写类的虚拟析构函数(Destroy)

但是我认为BigObject不是MyObject的所有者,所以BigObject根本不应该尝试释放它

正如CodeInChaos已经说过的,FreeAndNil只释放一个变量,在本例中是FMyObject字段。FreeAndNil不是必需的,因为在对象被释放后不会发生任何事情

无法使用Assigned检查对象是否已释放。它只能检查nil,FreeAndNil只能将一个引用设置为nil,而不是对象本身(这是不可能的)



因此,您的程序设计应该是,只有当没有任何对象正在访问它时,对象才能被释放。

您应该重命名您的方法
Free
TObject
中已经声明了这样的方法。重写
Destroy
并在内部调用
inherited
要好得多。@Uwe:是的,这是一个糟糕的名字选择。:)不过,由于你自己和其他人的众多评论/回答,我宁愿让它保持现状。谢谢简单回答:对象不能为零,只有对对象的引用可以:)更简单的代码示例<代码>A:=B;B:=零
注意,现在只有B
nil
,不一定是A.@CIC:谢谢你的解释(+1)。不过,我还是要归功于唐德雷,因为他是第一个。@CodeInChaos调解人事实上是第一个,但你应该接受最好的,而不是第一个,不管你认为什么是最好的。@David:看来我读的时间和你们不一样。您、CIC和TOndrejs的回答都令我满意,因此我愿意接受第一个正确的答案。@conciliator No,我们都有相同的时间。中投是第一个。我认为CIC的答案是最好的,这是我会接受的答案。谢谢你的回答,谢谢你关于Free和Destroy用法的建议。@David这两行并不完全等同于
FreeAndNil
,因为
FreeAndNil
先将引用置为零,然后再销毁对象。但在大多数情况下,这并不重要+1为好explanation@smasher是的,这是一个公平的观点,但我希望你能原谅我,帮助我顺利地解释@大卫·费弗南:非常学校风格的解释。。。非常喜欢+谢谢你迅速(准确)的回答,伙计。事后看来,很明显我感到很尴尬。。。最后期限似乎会让你(至少是我)过早发帖顺便说一句,公平地说,我不是第一个,@CodeInChaos在我面前发布了他的答案。我通常会说,特别是对于那些有内存管理问题的人:尽可能避免free和nil。它通常是不必要的,而且它通常不符合您希望它做的事情。好的代码可以不使用FreeAndNil。@rudy这通常是个错误的建议。使用FreeAndNil可以更容易地找到bug。不使用它不会。啊,旧的
FreeAndNil
vs
TObject.Free
讨论。。。这件事可以永远持续下去——谢谢鲁迪分享一些关于设计的深刻想法。