C + + 11还增加了防止从类继承或简单地防止在派生类中重写方法的能力。这是使用特殊标识符 final 完成的。例如:
struct Base1 final { };
struct Derived1 : Base1 { }; // ill-formed because the class Base1
// has been marked final
它还用于标记虚函数,以防止它在派生类中被重写:
struct Base2 {
virtual void f() final;
};
struct Derived2 : Base2 {
void f(); // ill-formed because the virtual function Base2::f has
// been marked final
};
error: only virtual member functions can be marked 'final'
将非虚方法标记为“ final”是没有意义的
struct A { void foo(); };
struct B : public A { void foo(); };
A * a = new B;
a -> foo(); // this will call A :: foo anyway, regardless of whether there is a B::foo
struct A { virtual void foo(); };
struct B : public A { virtual void foo(); };
struct C : public B { virtual void foo() final; };
struct D : public C { /* cannot override foo */ };
然后,final对可以进行多少重写设置了一个“底线”。其他类可以扩展 A 和 B 并覆盖它们的 foo,但是如果类扩展 C 则不允许。
// This pure abstract interface creates a way
// for unit test suites to stub-out Foo objects
class FooInterface
{
public:
virtual void DoSomething() = 0;
private:
virtual void DoSomethingImpl() = 0;
};
// Implement Non-Virtual Interface Pattern in FooBase using final
// (Alternatively implement the Template Pattern in FooBase using final)
class FooBase : public FooInterface
{
public:
virtual void DoSomething() final { DoFirst(); DoSomethingImpl(); DoLast(); }
private:
virtual void DoSomethingImpl() { /* left for derived classes to customize */ }
void DoFirst(); // no derived customization allowed here
void DoLast(); // no derived customization allowed here either
};
// Feel secure knowing that unit test suites can stub you out at the FooInterface level
// if necessary
// Feel doubly secure knowing that your children cannot violate your Template Pattern
// When DoSomething is called from a FooBase * you know without a doubt that
// DoFirst will execute before DoSomethingImpl, and DoLast will execute after.
class FooDerived : public FooBase
{
private:
virtual void DoSomethingImpl() {/* customize DoSomething at this location */}
};
Vtables 的一个主要缺点是,对于任何这样的虚拟对象(假设在典型的 Intel CPU 上是64位) ,指针本身就会占用缓存线路的25% (64字节中的8个)。在我喜欢编写的那种应用程序中,这种伤害非常严重。(根据我的经验,从纯粹主义的性能观点来看,这是反对 C + + 的第一个论点,也就是 C 程序员的观点。)
在需要极高性能的应用程序中(这对 C + + 来说并不罕见) ,这可能真的会变得非常棒,不需要用 C 风格手动解决这个问题或奇怪的模板杂乱无章。
这种技术被称为 虚拟化。一个值得记住的术语。 : -)
最近有一篇很棒的 Andrei Alexandrescu 演讲,很好地解释了如何在今天解决这种情况,以及“最终”如何成为未来“自动”解决类似问题的一部分(与听众讨论) :
#include <iostream>
using namespace std;
class Base
{
public:
virtual void myfun() final
{
cout << "myfun() in Base";
}
};
class Derived : public Base
{
void myfun()
{
cout << "myfun() in Derived\n";
}
};
int main()
{
Derived d;
Base &b = d;
b.myfun();
return 0;
}
另外:
#include <iostream>
class Base final
{
};
class Derived : public Base
{
};
int main()
{
Derived d;
return 0;
}
class IA
{
public:
virtual int getNum() const = 0;
};
class BaseA : public IA
{
public:
inline virtual int getNum() const final {return ...};
};
class ImplA : public BaseA {...};
IA* pa = ...;
...
ImplA* impla = static_cast<ImplA*>(pa);
//the following line should cause compiler to use the inlined function BaseA::getNum(),
//instead of dynamic binding (via vtable or something).
//any class/subclass of BaseA will benefit from it
int n = impla->getNum();