为什么不将对象默认为nil?

时间:2014-04-16 17:17:51

标签: delphi

在Delphi中,从TObject下降的变量的记录行为是默认值nil。但是,我遇到的情况并非如此。

通过IDE(F9)运行以下代码示例会产生混合结果

var
  objTemp : TMemDataSet;
begin
  if (objTemp = nil) then
     ShowMessage('Nil');
end;
  • 32位/调试模式,未默认为nil
  • 32位/释放模式,未默认为nil
  • 64位/调试模式, 默认为nil
  • 64位/发布模式,未默认为nil

我的理解是值应始终默认为零。

在XE2和XE5下也测试了相同的结果。

这是Delphi中的预期行为吗?

2 个答案:

答案 0 :(得分:11)

您的理解不正确。未初始化非托管类型(IOW,非引用计数类型)的局部变量。您必须先为它们分配一个值才能使用它们。

来自XE5 documentation(参见“声明变量”部分的底部 - 我在Wiin32中包含了类型拼写错误,但重点是我的):

  

如果没有显式初始化全局变量,那么编译器   将其初始化为0.对象实例数据(字段)也是   初始化为0. 在Win32平台上,本地的内容   变量未定义,直到为其赋值。

     

请注意,每当Emba写“Win32”时,它们指的是非ARC编译器,因此上述内容对Win64和OSX也有效。

您可以在帮助索引中使用搜索词Variables在Delphi 2007中找到相同的信息;它是“变量VBScript”和“变量[OpenGL]”之间的那个。

您在Win64调试版本中看到的差异可能只是由编译器完成的事情,幸运事故或完全不同的事情。不过,这应该不重要。如您所知,默认情况下不会初始化局部变量,只需确保在使用它们之前在所有情况下都这样做。执行并不困难;当你声明一个局部变量时,

var
  MyObj: TSomething;

您要么自己分配一个值,要么从代码中的其他地方收到一些值:

MyObj := TSomething.Create;   // Created yourself
MyObj := GetSomething();      // Function result
MyObj := Self.SomethingCollection[Self.SomethingCount - 1]; // Local ref

应该绝对没有理由需要依赖于初始化的局部变量,因为测试可以在分配给本地var之前在外部引用上完成,或者在分配外部之后在本地var上完成参考:

if SomethingIGot = nil then
  raise Exception.Create('Received a nil parameter');
MyObj := SomethingIGot;

// or

MyObj := SomethingIGot;
if not Assigned(MyObj) then
  raise Exception.Create('MyObj was assigned a nil value');

答案 1 :(得分:2)

Ken向您解释了如何,让我尝试解释为什么 ...

  

在Delphi中,从TObject下降的变量的记录行为是默认值nil。

你有一些困惑: 类的成员变量(即TObject的后代)初始化为0或nil。
但是,对象引用本身是否已初始化取决于上下文。

至少从德尔福2开始就是这种情况。

原因是速度优化:

局部变量是短暂的
本地(全局)变量存在于堆栈中 这种记忆结构不断重复使用相同的记忆 将变量初始化为nil(或0)不会为您节省任何工作,因为您应该将变量实例化为有用的东西。

procedure Test;
var
  MyObject: TMyObject;
begin   
  MyObject:= TMyObject.Create;  
  .....

在程序开始之前将它初始化为nil在这里显然没有用处,因为在你将它设置为非零值之前你不能使用它。
因为局部变量在声明它们的地方附近使用,所以几乎没有混淆的风险。

当程序结束时,局部变量超出范围 这实际上意味着这些变量曾经存在的内存空间在另一个过程中被重用。

物品可以长寿
创建对象时,系统会在堆上为其分配内存 Delphi使用自己的内存管理器。 对于所有对象,您可以确保在调用TObject.Create之后,对象的所有成员变量都设置为0(或零)。
正如David指出的那样,如果Create(从TObject进一步向下的部分)失败,这允许系统安全地释放实例。

它也有意义,因为否则你必须在你编写的每个构造函数中初始化大量变量;现在你只需要给非空成员一个值。

这在几个层面都有意义。
类可以有几十个或几百个成员变量 它可以防止错误 它允许处理构造函数中的错误。