“方法 '%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.Proc2
states 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' 的虚拟方法”。真正隐藏的是啥?的主要内容,如果未能解决你的问题,请参考以下文章