“方法 '%s' 隐藏了基本类型 '%s' 的虚拟方法”。真正隐藏的是啥?

Posted

技术标签:

【中文标题】“方法 \'%s\' 隐藏了基本类型 \'%s\' 的虚拟方法”。真正隐藏的是啥?【英文标题】:"Method '%s' hides virtual method of base type '%s'". What's really being hidden?“方法 '%s' 隐藏了基本类型 '%s' 的虚拟方法”。真正隐藏的是什么? 【发布时间】:2010-10-07 04:04:37 【问题描述】:

在阅读了Ian Boyd 的构造函数系列问题(1、2、3、4)后,我意识到我不太了解隐藏的字面含义。

我知道(如果我错了,请纠正我)override 的唯一目的是能够具有多态行为,以便运行时可以根据实例的实际类型解析方法 - 而不是到声明的类型。考虑以下代码:

type
  TBase = class
    procedure Proc1; virtual;
    procedure Proc2; virtual;
  end;

  TChild = class(TBase)
    procedure Proc1; override;
    procedure Proc2;            // <- [DCC Warning]
  end;

procedure TBase.Proc1;
begin
  Writeln('Base.Proc1');
end;
procedure TBase.Proc2;
begin
  Writeln('Base.Proc2');
end;

procedure TChild.Proc1;
begin
  inherited Proc1;
  Writeln('Child.Proc1');
end;
procedure TChild.Proc2;
begin
  inherited Proc2;
  Writeln('Child.Proc2');
end;

var
  Base: TBase;
begin
  Base := TChild.Create;
  Base.Proc1;
  Writeln;
  Base.Proc2;
  Base.Free;
  Readln;
end.

哪些输出:

Base.Proc1Child.Proc1Base.Proc2

TChild.Proc2states that 这个方法的警告“将隐藏对基类同名方法的访问”。我看到的是,如果我不覆盖Proc2,我就会失去该方法解析为其实际类型而不是其基本类型的能力。如何隐藏对 base 方法的访问?

进一步,作为解决该警告的警告的文档,说明:

首先,您可以指定覆盖 使派生类的过程也 虚拟的,因此允许继承 调用仍参考原件 过程。

现在,如果我从“TChild”(无多态性)创建一个“TChild”实例,则非覆盖方法中的继承调用显然是指原始过程。如果我从“TBase”创建“Child”实例,该调用甚至不会解析为“TChild”方法,我怎么能调用“Inherited”来引用任何东西?

我误会了什么?

【问题讨论】:

【参考方案1】:

除此之外,您将无法定义

TGrandChild = class(TChild) 
  procedure Proc2; override;
end; 

因为 TGrandChild 看到的 Proc2 是来自 TChild 的不是虚拟的。 TChild.Proc2 对后代隐藏 TBase.Proc2。

编辑:

回复 Sertac 的评论:

var 
  Base: TBase; 
  Child : TChild
begin 
  Child := TChild.Create;
  Base := Child;
  Base.Proc2; 
  Child.Proc2;

  Base.Free; 
  Readln; 

这将输出

Base.Proc2
Base.Proc2
Child.Proc2

所以,似乎两次调用同一个方法实际上是调用了 2 个不同的方法。这使得代码更难理解(这不实用)并产生意想不到的行为。

【讨论】:

错了。移除覆盖,从“TBase”创建一个“TGrandChild”实例,并在其上调用“Proc2”。该调用将解析为“TBase.Proc2”。 在前面的评论中,我的意思是写“覆盖指令”而不是“覆盖”。虽然不会对结果产生影响.. @Sertac - 肯是正确的。警告警告您的是 inability 从 TChild 的后代覆盖 TBase.Proc2。 Ken 编写的代码无法编译,因为 TChild.Proc2 不是虚拟的。显然,它是打算让 TBase.Proc2 被覆盖,但它不能被覆盖,因为它已经被 TChild.Proc2 隐藏了。 谢谢@Barry。从现在开始,每当我看到警告时,我都会阅读它:“方法 '%s' 从后代类中隐藏基类型 '%s' 的方法的虚拟性” 如果可以的话。 @Ken - 我想我从警告短语中得出的含义有问题。其实我从来没有想过可能的后代课程,谢谢你的看法。不过,对于“引用原始过程的继承调用” 的含义,我也很感激。他们什么时候不?【参考方案2】:

你想的太复杂了。隐藏并不意味着您完全无法访问原件。它只是意味着(并且您自己已经注意到这一点)如果您有一个静态类型 TChild 的对象,并且您在其上调用 Proc2,它会调用 TChild 中的对象,而不是 TBase 中的对象。肯说的也是真的。

这是警告你,因为原件是虚拟的,隐藏很可能不是人们在编写这样的代码时想要的。至少这是糟糕的编码风格。

【讨论】:

谢谢蒂莫,确实我认为隐藏意味着失去对基类方法的访问权......我不理解你的下一个陈述的意思。在声明为“TChild”的“Child”上调用“Proc2”当然会运行“TChild.Proc2”。这与“覆盖”/“隐藏”有关吗? 对不起,我的上一条评论搞砸了。一般来说,我会这样定义隐藏:您在某个范围内有一个标识符,并且您声明该标识符意味着其他东西。例如,您可以有一个全局变量 x,然后声明一个隐藏全局变量 x 的局部变量。这里也是类似的。考虑像“child.Proc2;”这样的代码其中孩子是 TChild。如果 Proc2 没有在 TChild 中重新声明,则它调用 TBase.Proc2。但是因为是重新声明,所以调用了 TChild.Proc2。【参考方案3】:

使用 'reintroduce' 取消警告。

【讨论】:

欢迎来到 Stack Overflow。您的参与热情令人鼓舞。但是下次,请记住在回答之前阅读问题。 Sertac 没有询问如何消除警告。 为什么reintroduce 不显示警告? @Ian 因为有时这是期望的行为。 reintroduce 的全部目的是抑制警告; reintroduce 什么都不做。

以上是关于“方法 '%s' 隐藏了基本类型 '%s' 的虚拟方法”。真正隐藏的是啥?的主要内容,如果未能解决你的问题,请参考以下文章