3

Consider a program that has a class Foo containing a function Foo::fn declared like this:

virtual void fn();

and a subclass of Foo called Bar. Will declaring Bar::fn like this:

virtual void fn() override final;

cause calls to fn in Bar or subclasses of Bar to be any more efficient, or will it just keep subclasses of Bar from overriding fn? If calls are made more efficient using final, what is the simplest, most efficient method to define Bar::fn such that its functionality is exactly that of Foo::fn?

Conduit
  • 2,675
  • 1
  • 26
  • 39
  • 1
    No. The compiler doesn't even know that `Bar` exists (assuming the most common case of it being in another translation unit with whole-program optimization disabled) when it's determining how to compile code that calls `Foo::fn`. Why are you worried about vtable size? – Cameron Feb 04 '15 at 22:43
  • I suspect the vtable stays the same size but the final marks it so the compiler will not allow overrides in derived classes – cppguy Feb 04 '15 at 22:43
  • 3
    There are typically no vtables in instances of classes. Instances contain vptrs. – n. m. could be an AI Feb 04 '15 at 22:46
  • 1
    A better question might be to ask when the vtable can be ignored. E.g. `void (Bar &b) { b.fn(); }`. This can ignore the vtable as it knows that `b.fn()` must call `B::fn`. – Aaron McDaid Feb 04 '15 at 22:56
  • 1
    No it won't make the vtable smaller. Even if you've declared it final, it still needs to be in the vtable since you may invoke it through a base class. – Mysticial Feb 04 '15 at 22:59

2 Answers2

6

If fn is defined as final in Bar, the compiler can dispatch calls to fn through a pointer or reference to Bar statically since it knows that Bar::fn is the final overrider. For example, this program fragment:

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(); }

compiles to (See gcc.godbolt.org for details):

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

the call in with_foo is dynamically dispatched (call *(%rax) is an indirect call) through the vtable, but the call in with_bar statically dispatches to Bar::fn().

The simplest method to make Bar::fn be the final overrider of Foo::fn without changing behavior is to define it to statically call Foo::fn:

struct Bar : Foo {
  void fn() final override { Foo::fn(); }
};
Casey
  • 41,449
  • 7
  • 95
  • 125
  • My knowledge of assembly is somewhat minimal, but this seems to be what I was looking for... In essence, this shows that the call to `Bar::fn` is more efficient when the instance of `Bar` is stored as a `Bar`, but there is no gain when it is stored as a `Foo`, correct? – Conduit Feb 12 '15 at 18:12
  • 1
    @Conduit Correct. In general, there is no way for a compiler to statically determine that a `Foo*` or `Foo&` actually refers to a `Bar` instance. – Casey Feb 12 '15 at 19:18
  • Makes sense given a little thought. Exactly what I was looking for then - thanks! – Conduit Feb 12 '15 at 19:20
2

I've not EVER cared about the size of the vtable. It is typically relatively small, and there is only one per class declaration. What is much more bothersome is extra space taken up in the class instances, since, except for singleons, class instances are often many of. So adding extra elements into a class, in some way or another, will definitely affect the amount of memory. If it's REALLY bothering you that the vtable is too large, then doing some redesign so that there aren't so many different virtual member functions (perhaps splitting the class hierarchy into several classes) or fewer derived classes. But really, even if you have hundreds of classes, each with a hundred virtual member functions, it's still relatively small - 200 classes with 100 members would take up 20000 * 8 bytes per entry [64-bit architecture] -> 160KB. Surely the 20000 functions [yes, in theory, you only need ONE new function per derived class to need a new vtable, but that is rather a silly design, so unlikely in reality]

The purpose of the final keyword is to ensure that you don't derive from it further - this is useful for example if you have a basic class hierarchy where some particular function should not be "changed". Say for example you have:

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; }
 };

You have to trick around a bit to ensure that user_base isn't used as a base-class for a fake_super_user, and of course, there are other BIG security issues with such a design, but it gives you some idea.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • I think this pretty much covers what I was looking for, assuming that I'm correct about you stating that `final` is only used to ensure subclasses can't override the function, but doesn't affect the outcome post-compilation. It sounds like I may be misusing my terminology... I'm inferring from your answer that vtables are kept with the class. What is stored with the actual instances? – Conduit Feb 05 '15 at 00:38
  • Never mind - found what I was looking for. Edited question to follow. – Conduit Feb 05 '15 at 00:41
  • 2
    The class contains a pointer to the vtable(s) for the instance. [You can have multiple vtables in cases where there is multiple inheritance] – Mats Petersson Feb 05 '15 at 01:08