在没有基类(未派生)的类中,给虚函数添加final关键字是否有意义?

28

我正在阅读一个很棒的C++11教程,作者在解释final关键字时提供了这个示例:

struct B {
    virtual void f() const final;   // do not override
    virtual void g();
};
struct D : B {
    void f() const;     // error: D::f attempts to override final B::f
    void g();       // OK
};

那么在这里使用 final 关键字是有意义的吗?我认为你可以避免在这里使用 virtual 关键字,并防止 f() 被覆盖。


3
我不想贬低它,因为大师可能会出现并提出一个使用案例,解决一些非常晦涩的编译错误... 但这看起来像是一件愚蠢的事情。 注:原文中的"guru"指的是专家或权威人士。 - StoryTeller - Unslander Monica
2
可能是将基类函数标记为虚拟和最终是否有意义?的重复问题。 - Toby Speight
实际上还有更多的重复,例如什么是最终虚函数的意义? - Ruslan
为什么会有三个关闭请求?这个问题为什么不是合法的? - curiousguy
3个回答

34
如果您不将函数标记为virtualfinal,那么子类仍然可以实现该函数并隐藏基类函数。通过将函数设置为virtualfinal,子类既不能重写也不能隐藏该函数。

3
我知道会有一位大师出现。尽管我觉得这样有点过于繁琐(可能会省略),但为了防止隐藏,仍然给一个赞。+1。 - StoryTeller - Unslander Monica
3
显然在这个问题上有共识,但就我而言,我想要一份解释,说明如何既不做任何有用的事情又破坏另一种语言特性会被认为是一种好处。 - Quentin
1
@Quentin 这样做可以消除歧义的可能性,例如在 template <DerivedFromB> void doStuff(DerivedFromB & b) { b.f(); } 的情况下,你可以百分之百确定 f() 调用的位置。 - Caleth
1
即使 b 的静态类型是 D@Caleth b.B::f(); 也会按照您的期望工作。 - Quentin
3
@Caleth说:class D : public B { virtual void f(int=0); }。谁说我不能"覆盖" B::f?Quentin的解决方案 b.B::f() 在这里是有效的。 - MSalters
显示剩余5条评论

11

是的!在您提供的示例中,final关键字会防止任何派生类覆盖f(),正如您所说。如果该函数是非虚拟的,D:f()允许隐藏基类版本的函数:

struct B {
    void f() const;   // do not override
    virtual void g();
};
struct D : B {
    void f() const; // OK!
    void g();       // OK
};

f()函数声明为virtualfinal,任何试图覆盖或隐藏该函数的尝试都会导致编译错误。


3
你的直觉是正确的:将一个函数设置为virtual,再立即使用final限制它与非虚函数相比没有任何好处。这只是一个简短的代码片段,用于演示该特性。
此外,正如其他答案中所述,这实际上会破坏函数隐藏 - 你永远无法在D或其派生类中具有相同参数列表的f函数。
当你决定限制f时,这是一种权衡。由于这里没有办法执行实际的虚拟调用,你实际上是处于劣势而没有任何好处。

1
我想到了名称隐藏。这样可以防止派生类隐藏f()。如果您在声明f()时没有使用virtual关键字,它可能会在派生类中被隐藏。 - Eduard Rostomyan
评论始终是受到赞赏的,不要点踩。 - Quentin
1
我认为你被踩的原因是你对于禁用名称隐藏的态度。虽然我无法立即列举出任何这样的用途,但我想这个功能实际上可以用于某些特殊目的。 - Ruslan
@Ruslan 好的,首先是与静态多态性有关的任何内容。我不知道,因为你不知道如何使用它们而禁用任意功能,并声称这样做更好,听起来对我来说非常短视。在函数隐藏方面是否存在类似于 goto 的禁忌,我可能会错过吗? - Quentin
我所说的"特性"并不是指"函数名称隐藏",而是指"禁用函数名称隐藏"。当然,我知道名称隐藏本身也有其用途。 - Ruslan
@Ruslan 哦,我的错 :) - Quentin

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