【问题标题】:override, virtual/dynamic in Delphi constructorsDelphi 构造函数中的覆盖、虚拟/动态
【发布时间】:2013-06-29 14:06:32
【问题描述】:

虚拟/动态的那个

// declare in Child Class
constructor Create; virtual;

constructor TChildClass.Create;
begin
  inherited;
end;

具有覆盖的那个。

// declare in Child Class
constructor Create; override;

constructor TChildClass.Create;
begin
  inherited;
end;

一无所有的人

// declare in Child Class
constructor Create;

constructor TChildClass.Create;
begin
  inherited;
end;

这些是一样的吗?看起来很混乱。

【问题讨论】:

  • 这个 Q 和构造函数有什么关系,还是和一般的 OOP 有关系?
  • 其实只是想知道这个delphi例子的具体区别。
  • 最好不要在问题中留下猜谜游戏。 “//声明”行是基类还是子类的那些部分?如果您在每种情况下都生成完整但最少的示例,那将是最好的。
  • 是的,都是子类中的构造函数。

标签: delphi methods constructor virtual-functions


【解决方案1】:

是的,这是有区别的,但让我们首先用更基本的 OOP 术语来处理 virtual 关键字,但仍然是它如何应用于 Delphi 方法。

当您声明派生(子)类并将方法实现为“覆盖”时,这意味着您正在覆盖(惊喜)基类的匹配方法。

这意味着你可以这样写代码:

var child : TBaseClass;
begin
    child := TChildClass.Create; // note that it is stored in TBaseClass variable
    child.VirtualMethodDefinedInBaseClassThatHasBeenOverriddenInChildClass;

这将调用子类中的方法,即使变量被定义为基类类型。这就是虚拟方法的全部目的,您可以通过更通用类型的引用来访问对象,并且仍然可以调用为您正在处理的特定类型的对象编写的方法。

如果您在基类中有一个虚方法,但您选择不在子类中重写,而是重新引入,那么在某些情况下您实际上是在替换它。请注意,在大多数情况下,您需要告诉编译器您确实打算这样做,尽管我不确定 Delphi 在这里需要什么。

基本上,如果您的变量是 TBaseClass 类型,并且您在其上调用了已在 TChildClass 中重新引入的虚方法,它仍将调用基类中的方法。

但是,如果您的变量是 TChildClass 类型,并且您在其上调用该方法,您将获得新方法。

现在,对于构造函数,在 Delphi 中,情况略有不同。

虚拟构造器的重点是能够虚拟地构造对象,为此,Delphi 也有“类类型”。

你可以这样说:

type TClassToUse = class of TBaseClass;
var cls : TClassToUse;
    obj : TBaseClass;
begin
    cls := TChildClass;
    obj := cls.Create;

(请注意我的 Delphi 知识在这里有点生疏,如果有人发现上述代码中的错误或明显问题,请告诉我或直接修复它)

这里我们将一个“类”存储在一个变量中,然后请该类为我们构造一个对象。这让我们可以切换出创建哪个类,但是我们还需要声明我们要使用virtual的构造函数,否则会出现问题。

所以在上面的代码中,如果你在 TBaseClass 中将构造函数声明为 virtual,然后在 TChildClass 中重写它(代码实际上在 cls 中使用),则将使用重写的构造函数。

另一方面,如果您没有将构造函数声明为虚拟,我们将回到基类构造函数。虚拟基本上意味着在运行时找出正确的方法来执行,而非虚拟将在编译时找出它。

上述正常方法所述的重新引入,也可以这种方式工作。

但是,虚拟构造函数仅在通过类类型使用时用作虚拟构造函数。

【讨论】:

  • +1。不错的答案。仅供参考:Delphi 确实包含一个用于重新引入的 reintroduce 关键字,用于定义与父类中的名称相同但参数不同的方法。
  • 从不需要重新引入,祖先中的方法被简单地隐藏了。但是,不对祖先中的虚拟(或动态)方法使用 reintroduce 将触发警告“方法 X 正在祖先 A 中隐藏虚拟方法 X”(类似这些行)。因此,如果您希望您的编译/构建发出零提示和警告(这是您应该想要的),那么您将需要使用 reintroduce。
  • @MarjanVenema 但最好是必需的。我猜在早期版本中,隐藏被认为是有害和危险的,无论如何都值得警告。当他们决定让它成为一种合法的外来做法时,由于向后兼容性,他们无法执行它......
  • @Arioch'The:是的,如果总是需要重新引入,我也更喜欢它。事实并非如此,这可能会导致一些难以追踪的错误和 AV。
【解决方案2】:

不,静态方法和虚拟方法不是一回事。

overridevirtual方法的一个例子。

http://en.wikipedia.org/wiki/Virtual_function

http://docwiki.embarcadero.com/RADStudio/XE4/en/Methods#Method_Binding

构造函数在这里并没有什么特别之处——它们与问题的其他方法遵循相同的规则

【讨论】:

  • 无论构造函数后面的关键字是什么,继承的都可以在构造函数中,对吧?
  • 是的。 Inherited 只是调用祖先类的方法,而不管后代中的任何东西。
  • @Rob: 更正:虽然这对于构造函数来说并不实际,但一般来说,inherited 调用祖先的方法如果有一个并且什么都不做(无操作)如果没有 - 这对abstract 方法有影响,
  • @RobKennedy ....上面的评论涉及使用 inherited; 关键字作为一种特殊的指令,在这种“快捷方式”的情况下,“继承”语句的作用就像是不费吹灰之力原谅存根。但是inherited 也可以用作修改为常规调用,当其后跟一些方法名称时,如inherited Create(); - 那么实现应该存在。
  • 我知道。无论如何,我关于它的行为与后代无关的说法仍然正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
相关资源
最近更新 更多