override
和 reintroduce
指令之间有什么区别?我应该何时不在重写方法中使用 inherited
关键字?
答案 0 :(得分:45)
对Jim的答案的引用,这是非常好的BTW,只描述了使用指令的时间和地点。答案的另一部分是他们为什么还需要?没有它们,许多语言相处得很好,对吗?在设计Delphi Object Pascal语言的各个方面时,OOP(面向对象编程)已经成为主流多年。在此期间,人们观察到使用许多采用这些概念的语言(Turbo Pascal,C ++等)来开发应用程序框架时,我所谓的“版本2”问题就会受到影响。
假设您使用语言X开发了一个非常棒的框架,并将其作为版本1发布。您的用户竭尽所能,并且使用频繁。刷新成功,你决定发布版本2更加棒极了。您特别确保它完全向后兼容。突然,您的用户开始报告奇怪的行为。他们自己的虚拟方法在奇怪的时候被调用。许多人报告他们的旧代码无法使用新版本进行编译。奇怪。所有相同的对象,方法和功能仍然存在。您所做的只是为一些基类,一些新对象类型和一些新的可选功能添加一些虚方法。发生了什么事?
覆盖和重新引入指令用于消除此问题,要求为了实际覆盖虚拟方法,必须使用override指令代替virtual指令。如果您碰巧引入了自己的虚拟方法,该方法与您的祖先的一个虚拟方法具有相同的名称,编译器现在会警告您,但仍然会做正确的事情。在这种情况下,使用重新引入,不仅可以抑制该警告,还可以在您打算执行此操作的源文档中进行记录。
如果没有覆盖和重新引入指令,您将无法不断改进框架而不必担心会破坏所有用户。如果您的用户每次发布新版本时都必须进行大量修改,那么他们就不愿意采用新版本。最后,使用“覆盖”还允许框架设计者在不破坏用户代码的情况下更改祖先中的虚拟类型。例如,在Delphi中,许多方法都标记为“动态”,这是一种基于表的运行时方法查找形式的多态性。它的执行速度不如普通虚拟机快,因此它通常用于很少被覆盖的方法和/或对用户操作的响应,其中额外的开销从未被注意到。假设在框架的V1中,一个方法被标记为“动态”,但在实践中,它最终被覆盖并被调用超出您的预期。在V2中,您可以将其更改为“虚拟”,而不必担心用户代码被破坏。
Delphi的Object Pascal语言不是识别此问题的唯一语言。出于完全相同的原因,C#需要使用“override”指令。 C ++标准委员会终于认识到了这个问题,正在修改语言以支持它......有点儿。在C ++中,如果方法的名称和参数列表与祖先的虚拟匹配,那么它就是一个覆盖(即使你没有在后代上说“虚拟”!)。对于即将推出的新C ++标准,如果指定“虚拟”且签名不匹配,则它是当前类引入的新虚拟方法。如果与祖先有签名匹配并且编写者没有打算覆盖,那么“new”关键字用于告诉编译器这是 new 这个班的虚拟。
答案 1 :(得分:11)
覆盖指令用于覆盖继承类中的虚方法。
重新引入指令用于声明一个方法,该方法与超类中的名称相同,但参数不同。
答案 2 :(得分:6)
什么时候不应该在重写方法中使用inherited关键字?
基本上,答案是当你不希望继承的方法执行时。但要小心,因为不允许继承的方法运行可能会以不合需要的方式破坏继承对象的功能,因此请确保不会引入任何意外的副作用。
例如,假设您希望完全覆盖名为ApplyDiscount的继承函数,但有人已将折扣百分比硬编码到祖先类中。如果您调用继承的ApplyDiscount,它将覆盖您的代码或计算您将覆盖的值;在这种情况下,您可以不调用继承并自行应用折扣。
(这是一个人为的例子,所以如果有人能想到更好的一个,请加上它。)
答案 3 :(得分:1)
在许多情况下,您不希望在重写方法中调用inherited。
在我使用的一些库中,基本方法抛出错误(ENotImplimented或类似)。显然,在这种情况下,你不想调用inherited,否则你的代码也会抛出错误。
我的一些类有一个默认实现,适用于大多数情况。该方法仅被覆盖以替换默认值,并且无需调用默认值。
例如,我的基础财务对象的GST功能(=销售税)返回ExGst金额的12.5%,IncGst返回ExGst + GST。
在我的收入补偿对象上,GST始终返回0,因此无需调用继承的函数。