最终虚拟功能的重点是什么?

fre*_*low 53 c++ inheritance virtual-functions final c++11

维基百科在C++ 11 final修饰符上有以下示例:

struct Base2 {
    virtual void f() final;
};

struct Derived2 : Base2 {
    void f(); // ill-formed because the virtual function Base2::f has been marked final
};
Run Code Online (Sandbox Code Playgroud)

我不明白引入虚拟功能并立即将其标记为最终功能.这只是一个不好的例子,还是有更多的东西?

bam*_*s53 58

通常final不会在基类的虚函数定义中使用.final将由覆盖该函数的派生类使用,以防止进一步的派生类型进一步覆盖该函数.由于覆盖函数必须是正常的虚拟,这意味着任何人都可以在另一个派生类型中覆盖该函数.final允许一个人指定一个覆盖另一个但不能被覆盖的函数.

例如,如果您正在设计类层次结构并需要覆盖函数,但您不希望允许类层次结构的用户执行相同操作,那么您可能会在派生类中将函数标记为final.

  • 我没有看到任何理由,但我也没有看到任何强有力的理由,因为你已经可以标记函数`final override`.也许应该有一个样式警告,要求所有`final`函数也被标记为`override`,就像其他`virtual`和`override`样式警告一样. (4认同)
  • 如果`final` 隐含地表示`override final`,这难道没有意义吗? (2认同)
  • @Trass3r 不可能实现“final”接口方法,因为在 C++ 中实现接口方法需要重写。这样一个无法实现的接口是没有用的。 (2认同)

Ant*_*ony 9

它对我来说似乎没用.我认为这只是演示语法的一个例子.

一种可能的用法是,如果你不希望f真正被重写,但你仍然想要生成vtable,但这仍然是一种可怕的做事方式.

  • @ sasha.sochka我认为例如`dynamic_cast`没有一个就行不通.但是,确保vtable的通常方法是使析构函数成为虚拟的. (3认同)

Pau*_*ney 8

对于要标记的函数,final它必须是virtual,即在C++11§10.3段中.2:

[...]为方便起见,我们说任何虚函数都会覆盖自己.

和第4段:

如果某个类B中的虚函数f用virt-specifier final标记,并且在从B派生的类D中,函数D :: f会覆盖B :: f,则该程序是格式错误的.[...]

ie,final只需要与虚函数(或使用类来阻止继承)一起使用.因此,该示例需要virtual用于它是有效的C++代码.

编辑:完全清楚:"点"询问关于虚拟甚至使用的问题.使用它的底线原因是(i)因为代码不会编译,并且,(ii)为什么在一个人满足时使用更多类使示例更复杂?因此,使用具有虚拟最终函数的一个类作为示例.


AJe*_*Jed 8

添加到上面的好答案 - 这是一个著名的 Final 应用程序(很大程度上受到 Java 的启发)。假设我们在基类中定义了一个函数 wait(),并且我们希望在其所有后代中实现 wait() 一个。在这种情况下,我们可以将 wait() 声明为final。

例如:

class Base { 
   public: 
       virtual void wait() final { cout << "I m inside Base::wait()" << endl; }
       void wait_non_final() { cout << "I m inside Base::wait_non_final()" << endl; }
}; 
Run Code Online (Sandbox Code Playgroud)

这是派生类的定义:

class Derived : public Base {
      public: 
        // assume programmer had no idea there is a function Base::wait() 

        // error: wait is final
        void wait() { cout << "I am inside Derived::wait() \n"; } 
        // that's ok    
        void wait_non_final() { cout << "I am inside Derived::wait_non_final(); }

} 
Run Code Online (Sandbox Code Playgroud)

如果 wait() 是纯虚函数,那么它将是无用的(并且不正确)。在这种情况下:编译器将要求您在派生类中定义 wait()。如果你这样做,它会给你一个错误,因为 wait() 是最终的。

为什么最终函数应该是虚函数?(这也让人困惑)因为(imo)1)final的概念和虚函数的概念非常接近【虚函数有多种实现——final函数只有一种实现】,2)很容易实现final效果使用虚函数表。


Luc*_*ore 6

我不明白引入虚函数并立即将其标记为最终函数的意义。

该示例的目的是说明如何final工作,它就是这样做的。

一个实际目的可能是查看 vtable 如何影响类的大小。

struct Base2 {
    virtual void f() final;
};
struct Base1 {
};

assert(sizeof(Base2) != sizeof(Base1)); //probably
Run Code Online (Sandbox Code Playgroud)

Base2可以简单地用于测试平台细节,没有必要覆盖,f()因为它只是用于测试目的,所以它被标记为final. 当然,如果你这样做,设计上就有问题。我个人不会创建一个带有virtual函数的类只是为了检查vfptr.


Ric*_*lly 5

在重构遗留代码时(例如,从母类中删除一个虚拟方法),这有助于确保没有子类使用这个虚拟函数。

// Removing foo method is not impacting any child class => this compiles
struct NoImpact { virtual void foo() final {} };
struct OK : NoImpact {};

// Removing foo method is impacting a child class => NOK class does not compile
struct ImpactChildClass { virtual void foo() final {} };
struct NOK : ImpactChildClass { void foo() {} };

int main() {}
Run Code Online (Sandbox Code Playgroud)