struct B1 final { };上面的代码是错误的,因为 D1 试图继承 B1,而 B1 则声明为 final。很像 Java,不是吗?当然!还有另外的用法:
struct D1 : B1 { }; // 错误!不能从 final 类继承!
struct B2{ virtual void f() final {} // final 函数};struct D2 : B2{ virtual void f() {} // 错误!};
这段代码又会出错,因为D2::f重写了B2::f,但是B2::f却被声明为 final 的!
我总觉得 C++中虚函数的设计很差劲,因为时至今日仍然没有一个强制的机制来标识虚函数会在派生类里被改写。vitual关键字是可选的,这使得阅读代码变得很费劲。因为可能需要追溯到继承体系的源头才能确定某个方法是否是虚函数。为了增加可读性,我总是在派生类里也写上virtual关键字,并且也鼓励大家都这么做。即使这样,仍然会产生一些微妙的错误。
下面再看另外一段代码:
struct B3开发 D3 的程序员真的想重写B3::f函数吗?还是说,他只是不小心写了个与父类同名的函数,却在不经意间导致了覆盖?为了避免这种错误,C++ 11 引入了override关键字(多么像 C# 啊!)。于是,我们会发现,下面的一段代码是会出错的:
{
virtual void f() {}
};
struct D3 : B3
{
void f() {}
};
struct B4{多亏了override关键字,我们可以让编译器帮我们检测到这个很难发现的程序错误。这段代码的错误在于,override关键字表明,g(double)虽然想要进行override的操作,但实际父类并没有这么个函数。
virtual void g(int) {}
};
struct D4 : B4
{
virtual void g(int) override {} // OK
virtual void g(double) override {} // Error
};
值得注意的是,这些并不是一些语法糖,而是能确确实实地避免很多程序错误,并且暗示编译器可以作出一些优化。调用标记了final的virtual函数,例如上面的B2::f,GNU C++ 前端会识别出,这个函数不能被覆盖,因此会将其从类的虚表中删除。而标记为final的类,例如上面的 B1,编译器则根本不会生成虚表。这样的代码显然更有效率。