Delphi中override的意外行为

5

启动你的时间机器,回到古代:这是Delphi,现在是2007年。

在我们的初学者手册中,我将一些编程基础(使用Delphi)写下来。在这个过程中,我发现了一个我不太理解的怪事。为了演示继承,我编写了一个基类和它的一个子类。一个函数被子类重新引入,另一个函数被重写。

unit UOverride;
interface
type
Base = class(TObject)
  strict private
    const
      CName: String = 'Base-Class';
      CMaxim: String = 'Somebody set up us the bomb.';
  public
    function GetName(): String; virtual;
    function GetMaxim(): String; virtual;

end;

Heir = class(Base)
  strict private
    const
      CName: String = 'Heir-Class';
      CMaxim: String = 'All your Base are belong to us!';
  public
    function GetName(): String; reintroduce;
    function GetMaxim(): String; override;
    function GetBaseName(): String;
    function GetBaseMaxim():String;
end;

implementation
{ Base }
function Base.GetMaxim: String;
begin
  Result := CMaxim;
end;

function Base.GetName: String;
begin
  Result := CName;
end;

{ Heir }
function Heir.GetMaxim: String;
begin
  Result := CMaxim;
end;

function Heir.GetName: String;
begin
  Result := CName;
end;

end.

当我创建一个Base对象并调用两个函数时,它按预期工作:
- GetName() => "Base-Class" - GetMaxim() => "Somebody set up us the bomb."
当我创建一个Heir实例时,同样也是这样:
- GetName() => "Heir-Class" - GetMaxim() => "All your Base are belong to us!"
现在,我将Heir实例转换为Base并再次调用两个函数。
- GetName() => "Base-Class",这没有疑问 - GetMaxim() => "All your Base are belong to us!",因为它被Heir重写。
出于好奇,我还添加了两个函数到Heir中。
function Heir.GetBaseName: String;
begin
  Result := inherited GetName();
end;

function Heir.GetBaseMaxim: String;
begin
  Result := inherited GetMaxim();
end;

我希望 GetBaseName() 能够给我基类 - 它确实这样做了。对于 GetBaseMaxim(),我不确定。在我看来,有三种可能性:
  1. 它无法编译并抛出错误或
  2. 它运行并将因为被覆盖的继承函数而失败,或者
  3. 最有可能的是,它会给我来自Heir的GetMaxim(),告诉我All your Base are belong to us!
但是,以上情况都没有发生!我从我的基类中得到了Somebody set up us the bomb.?为什么?我认为这个函数已经被Heir覆盖了!这不是什么大问题,但我不太理解这种行为,因为它不像我预期的那样工作。有人能解释一下吗?

请提供一个 [mcve]。 - David Heffernan
3
如果你调用inherited something,你会得到直接祖先(在这种情况下是基类)的something。我不会期望其他结果。请注意,inherited绕过了虚拟机制。这是必须的。否则,你总是会得到最派生的something,而如果使用inherited,这正是你不想要的。 - Rudy Velthuis
我要求看完整的代码的原因是,跟踪摘录很困难,可能会缺少代码、声明和调用代码。一个简单完整的控制台应用程序可以消除所有疑虑。 - David Heffernan
1
代码不在对象中。它是存在的,如果你调用inherited,你直接调用它。 - Rudy Velthuis
显示剩余3条评论
2个回答

5

Inherited 给祖先函数提供了非虚拟派发,这正是您观察到的。通常,在子类中使用它的函数与被调用的继承函数具有相同的名称,但正如您所示,这个习惯并不是必须的。您可以从子类的任何地方非虚拟调用任何继承方法。


所以这只是对概念实现方式的误解。感谢您的帮助! - Stephan
是的 - 看起来你期望inherited会产生'heir'中的方法。我可以理解你为什么这样想,但实际上它的意思相反 - 它是祖先提供给'heir'的方法。 - Hugh Jones

5
这并不是什么特别的东西。使用inherited可以请求实现基类。文档说:

如果在inherited后跟随成员名称,则表示对属性、字段或普通方法的调用或引用,但查找对应成员的操作从包含当前方法的类的直接祖先开始。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接