【问题标题】:Is there any sense in marking a base class function as both virtual and final? [duplicate]将基类函数标记为虚拟函数和最终函数有什么意义吗? [复制]
【发布时间】:2013-05-20 07:13:16
【问题描述】:

在 C++11 的 final 关键字的 various explanations 中,我看到了这样的示例。

class base
{
public:
    virtual void f() final;
};

class derived : public base
{
public:
    virtual void f();    // Illegal due to base::f() declared final.
};

这实际上是final 的有用用法吗?为什么要在基类中声明一个虚函数(暗示它可以在派生类中有用地覆盖)然后立即将其标记为final(否定该暗示)? virtual void f() final有什么用处?

我可以看到标记derived::f() final 而不是base::f() 的价值。在这种情况下,base::f() 可能有一个很好的基于设计的理由说明为什么f() 应该是虚拟的,而derived::f() 有一个很好的基于设计的理由说明为什么没有进一步的派生类应该覆盖其实现。

如果你不希望函数被多态覆盖,为什么不直接去掉 virtual 关键字呢?当然,派生类仍可能以非多态方式覆盖该函数。因此,基类中virtual void f() final 的目的是使base::f() 以任何方式都不可覆盖——无论是作为虚拟函数还是非虚拟函数?如果是这样,那么我们必须在这种情况下添加virtual 关键字才能启用final 的使用,这似乎有点不幸。我认为将非虚拟函数标记为最终函数应该是合法的。

virtualfinal 的含义似乎矛盾时,为什么要对源自基类的函数使用virtual void f() final

【问题讨论】:

  • 例子是examples;它们旨在显示该功能的作用,而不一定是如何使用它。这种方式只使用两个类来显示final 的含义。要使用真实世界的示例将需要 三个:一个启动虚拟,一个从它派生并成为 final,第三个尝试从它派生并重载它。这种方式更短。
  • @NicolBolas 我理解示例的目的。但是,这种特殊类型的示例是否也展示了一种有用的使用范式?就是这个问题。

标签: c++ c++11 virtual final


【解决方案1】:

您可以将其标记为virtual 以表明它在您继承的类中是“虚拟的”(即使您不必这样做),并将其标记为final 以表明没有派生类来自您的班级可能会进一步覆盖它。例如,当您实现抽象基类时,可能会发生这种情况。这是 C++11,所以没用; override 是一个更好的指示,因为它是由编译器强制执行的。

另一种可能性:您希望此方法不被覆盖,但您希望能够在不重新编译的情况下更改它。请记住,virtual 表示它在虚拟表中。即使编译器不允许你覆盖它。

我认为您展示的示例的目的是展示virtual 和final 之间的优先级,仅此而已。这是final最小用法,没有用处。

将非虚拟方法标记为final 毫无意义,因为无论如何您都无法覆盖它们。如果您希望编译器防止隐藏,那是一个完全不同的问题,与final 的方法无关。从某种意义上说,非虚方法已经是最终的了,但可以隐藏。

【讨论】:

  • 将函数标记为virtual 并不表示它是基数中的virtual。它表明它在被定义的类中是virtual,不管基类可能做了什么,也可能没有做。
  • @Pete Becker:我认为他的意思是让代码自我记录。在覆盖基类时(尤其是在 C++11 添加覆盖说明符之前)重新指定 virtual 关键字是一种常见的做法,以便阅读 detived clasd 定义的程序员知道特定方法是虚拟的。
  • @SeanMiddleditch - 这显然不是他所说的。
  • 这根本不是“清楚”,但是,嘿,英语。这很奇怪。
  • @PeteBecker,也许这不是你所理解的,但这正是我的意思。我认为“指示”足够清楚;事实证明不是,所以如果您有更好的表达方式,欢迎您相应地编辑答案。
【解决方案2】:

替代方法是非虚拟函数。但是非虚函数可以被派生类隐藏。所以如果你想防止函数隐藏,可以指定为virtual final。

【讨论】:

    【解决方案3】:

    将基类函数标记为 virtual 和 final 有什么意义吗?

    是的,至少是暂时的。

    我发现自己处于一个相对庞大且不熟悉的现有 C++ 源代码库中。大部分代码是在 C++11 之前编写的。我发现我想确保在基类中对虚函数的所有覆盖都标有override。困难的部分是找到所有这些覆盖。

    我在基类中用final 标记了虚函数,编译器很快就向我展示了每个覆盖的声明位置。然后很容易按照我想要的方式装饰覆盖,并从基类的虚拟中删除final

    【讨论】:

      猜你喜欢
      • 2012-07-27
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 2011-06-16
      • 2012-05-19
      • 2012-10-06
      • 2012-01-08
      • 1970-01-01
      相关资源
      最近更新 更多