虚拟功能效率和最终'关键词

时间:2015-02-04 22:39:11

标签: c++ performance c++11 vtable

考虑一个程序,其中包含一个类Foo,其中包含一个如此声明的函数Foo::fn

virtual void fn();

Foo的子类Bar。将声明Bar::fn这样:

virtual void fn() override final;

导致fn中的BarBar的子类调用更高效,或者只是保留Bar的子类来覆盖fn ?如果使用final提高调用效率,那么定义Bar::fn的最简单,最有效的方法是什么,以使其功能正好是Foo::fn

2 个答案:

答案 0 :(得分:4)

如果在fn中将final定义为Bar,则编译器可以通过指针或fn静态调度Bar来调用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(); } 是最终的覆盖者。例如,这个程序片段:

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

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

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

with_foo

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

使Foo::fn成为Foo::fn的最终覆盖而不改变行为的最简单方法是将其定义为静态调用struct Bar : Foo { void fn() final override { Foo::fn(); } };

{{1}}

答案 1 :(得分:3)

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

final关键字的目的是确保您不会进一步从中获取 - 例如,如果您有一个基本的类层次结构,其中某些特定的函数不应该是&#34,那么这很有用。 ;变更&#34 ;.比如你说:

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的基类,当然,还有其他一些大的安全问题。设计,但它给你一些想法。