InstanceClass.NewInstance vs InstanceClass.Create

时间:2012-05-05 10:22:33

标签: delphi oop vcl

InstanceClass.NewInstance + Instance.Create和InstanceClass.Create之间有什么不同;

方法一:

Instance := TComponent(InstanceClass.NewInstance);
Instance.Create(Self);

方法2:

Instance := InstanceClass.Create(Self);

哪个更好?

3 个答案:

答案 0 :(得分:10)

如果合适,我会一直使用InstanceClass.Create - 并且总是如此。

有很多原因。一个非常好的是单行版本更简洁。另一个是单行版本是标准的常用方法。

另一个原因是在方法1无法正确管理的构造函数中处理异常。如果发生异常,新实例将被销毁,但实例变量仍被分配给。这是与方法2的重要区别,并且违反了Delphi的所有生命周期管理约定。

你提到TApplication.CreateForm。我们来看看它:

Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
try
  Instance.Create(Self);
except
  TComponent(Reference) := nil;
  raise;
end;

请记住,Reference是您作为var参数传递的表单变量。关于这一点的一点是这段代码在调用构造函数之前分配了表单变量。通常,只有在构造函数完成后才会进行赋值。

据推测,这是因为引用表单变量(通常是全局变量)的代码即使从该表单的构造函数内部调用也可以工作。这是一个非常特殊的案例,绝大多数是例外而不是规则。不要让这个特殊情况驱动你的主流编码风格。

答案 1 :(得分:4)

(我添加了这个答案,因为IMHO其他人没有完成)

方法2是正确的。

方法1如果永远不会被调用,因为构造函数调用有一个隐藏参数,这可能会失败正确的iniatialize:事实上,NewInstance是一个每类伪虚拟方法!

实际上,构造函数调用时存在隐藏的boolean参数(寄存器EDX,因为EAX = class)。正如official documentation所述:

  

构造函数和析构函数使用与其他调用约定相同的调用约定   方法,除了传递一个额外的Boolean标志参数   表示构造函数或析构函数调用的上下文。

     

构造函数调用的flag参数中的值False表示构造函数是通过实例对象调用的   或使用继承的关键字。在这种情况下,构造函数的行为   像一个普通的方法。 a的flag参数中的值True   构造函数调用表明构造函数是通过一个调用的   课堂参考。在这种情况下,构造函数创建一个实例   由class给出的Self,并返回对新创建的引用   EAX中的对象。

特别是,当这样调用时,类不会调用_ClassCreate函数。如果不使用默认的NewInstance函数创建类,则可能无法初始化类。实际上,此函数被插入到类VMT中:在极少数情况下,它可能会过载(例如,提供另一种内存分配模式 - 可能是垃圾收集器或某些速度优化的分配器)。因此,在某些边境案例中,直接调用InstanceClass.NewInstance可能会出错。

function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
  ...
    TEST    DL,DL
    JL      @@noAlloc
    CALL    dword ptr [EAX].vmtNewInstance
@@noAlloc:
  ...

因此,直接调用InstanceClass.NewInstance只是为了有目的地完成,只有当你想要取消两个被覆盖的vmtNewInstance / vmtFreeInstance时(在这种情况下,你可能不得不调用.Free / .Destroy },但你拥有无记忆功能)。所以:永远不要调用NewInstance,而是调用Embarcadero(以及 FreePascal 团队)设计和记录的constructor,除非你需要做一些内部的低级别调整。

永远不要使用方法1来创建对象!它可能在99.9%的时间内工作,但在某些情况下可能会失败,或者在将来使用编译器/ RTL增强(如垃圾收集器)。即使VCL有时使用NewInstance,你也不应该使用它 - 我宁愿让这种方法受到保护。

答案 2 :(得分:2)

第二个更好,因为它是创建类实例的标准方法,而构造函数中应该使用过程形式来调用继承的构造函数。

相关问题