虚函数效率和'final'关键字

Virtual function efficiency and the 'final' keyword

考虑一个 class Foo 包含函数 Foo::fn 声明如下的程序:

virtual void fn();

Foo 的子 class 称为 Bar。将这样声明 Bar::fn

virtual void fn() override final;

导致调用 Bar 中的 fnBar 的子 class 更有效,或者它会保持子 class Bar 的 es 从覆盖 fn?如果使用 final 使调用更高效,定义 Bar::fn 使其功能与 Foo::fn 完全相同的最简单、最有效的方法是什么?

我从来不关心 vtable 的大小。它通常相对较小,每个 class 声明只有一个。更麻烦的是在 class 个实例中占用了额外的 space 个,因为除了单身人士外, class 个实例通常很多。因此,以某种方式向 class 添加额外的元素肯定会影响内存量。如果 vtable 太大真的困扰你,那么做一些重新设计,这样就没有那么多不同的虚拟成员函数(可能将 class 层次结构分成几个 classes)或更少的派生classes。但实际上,即使您有数百个 classes,每个都有一百个虚拟成员函数,它仍然相对较小 - 200 个 classes 和 100 个成员将占用每个条目 20000 * 8 个字节 [64位架构] -> 160KB。当然是 20000 个函数 [是的,理论上,每个派生只需要一个新函数 class 就需要一个新的 vtable,但这是一个相当愚蠢的设计,在现实中不太可能]

final 关键字的目的是确保您不会从中进一步派生 - 这很有用,例如,如果您有一个基本的 class 层次结构,其中某些特定功能不应该是 "changed"。比如说你有:

class user_base
{
    public:
      virtual bool check_password(); {... magical code .. };
      virtual bool is_super_user() = 0;
};

class superuser : public user_base
{
    public:
      virtual bool check_password() final  
        { .... magical code ...
          ... extra checks to ensure no remote login... 
        }
      virtual bool is_super_user() final { return true; }

 };

 class user : public user_base
 {
  public:
      virtual bool is_super_user() final { return false; }
 };

您必须花点时间确保 user_base 不被用作 fake_super_user 的基础 class,当然,还有其他重要的安全措施这样的设计有问题,但它给了你一些想法。

如果 fnBar 中定义为 final,编译器可以通过指针或对 Bar 的引用静态地调度对 fn 的调用,因为它知道 Bar::fn 是最终的替代者。比如这个程序片段:

struct Foo {
  virtual void fn();
};

struct Bar : Foo {
  void fn() final override;
};

void with_foo(Foo& o) { o.fn(); }
void with_bar(Bar& o) { o.fn(); }

编译为 (See gcc.godbolt.org for details):

with_foo(Foo&):
    subq    , %rsp
    movq    (%rdi), %rax
    call    *(%rax)
    addq    , %rsp
    ret

with_bar(Bar&):
    subq    , %rsp
    call    Bar::fn()
    addq    , %rsp
    ret

with_foo 中的调用通过 vtable 动态调度(call *(%rax) 是间接调用),但 with_bar 中的调用静态调度到 Bar::fn()

在不改变行为的情况下使 Bar::fn 成为 Foo::fn 的最终替代者的最简单方法是将其定义为静态调用 Foo::fn:

struct Bar : Foo {
  void fn() final override { Foo::fn(); }
};