0

So I know that in c++ virtual methods are for each class stored in table and each instance has a pointer that points to that table. So my question how subclass table looks like. I will provide an assembler directive:

vtable for Derived:
    .long   0
    .long   _typeinfo for Derived
    .long   _Derived::set()
    .globl  _vtable for Base
    .section    .rdata$vtable for Base,"dr"
    .linkonce same_size
    .align 4

So, from here I can see that Derived has one virtual method which is set(), but the part which bugs me is vtable for Base. Does Derived vptr holds pointer to Base vptr or is it stored inside of Derived's vtable. I've noticed that in code compiler just stores vtable at 0 address of object, once in Base constructor and once in Derived. Why isn't vtable overwritten?

P.S. I don't really understand directives

EDIT:

class Base{
   virtual void print() {
       printf("Base");
   }
}

class Derived : Base{
   virtual void print(){
       printf("Derived");
   }
}
  • Maybe it is overwritten. But if base stores first and derived second then it's overwritten with the correct value. – Jester Mar 26 '20 at 22:21
  • 1
    @ErikEidt at least in g++ code, the constructor writes the vtable pointer into the object. So base constructor writes base vtable into the object and then the derived constructor overwrites that with the derived vtable. – Jester Mar 26 '20 at 22:22
  • 1
    I don't know much about directives, but I think that ``.globl`` is just a matter of symbol visibility; ``vtable for Derived`` is only made of the tree first ``.long`` in terms of stored information. – prog-fh Mar 26 '20 at 22:24
  • 1
    It was pretty clear to me that's what he asked: _"compiler just stores vtable at 0 address of object, once in Base constructor and once in Derived. Why isn't vtable overwritten?"_. He has seen that both constructors write to the same location. I explained how that works. – Jester Mar 26 '20 at 22:26
  • @Jester Yes you understood what I asked, sorry for bad formating the question. Thank you for clarification. – Antonio Antolcic Mar 26 '20 at 22:29
  • "_an assembler directive_" It would be nice to have the source code that produces that vtable content. – curiousguy Apr 02 '20 at 20:06

1 Answers1

5

Simple answer is: it depends on the compiler. The standard does not specify how this should be implemented, only how it should behave.

In practice, implementations tend to simply have a derived vtable which begins with the same structure as the base vtable, than appends the derived class methods on the end (that is to say new methods in the derived class, not overrides).

The vtable pointer just points the the beginning of the whole table. If the object is being accessed through a base pointer type, then no one should ever look beyond the end of the base class methods.

If the pointer is of the derived type, then the same pointer will allow access further down the table to virtual methods declared in the derived class.

ADDENDUM: Multiple Inheritance

Multiple inheritance follows the same basic concepts, but quickly becomes complex for obvious reasons. But there's one important feature to bear in mind.

A multiply derived class has one vtable pointer for each of its base classes, pointing to different vtables or different locations in the same vtable (implementation dependent).

But it's important to remember it's one per immediate base class per object.

Thus if you had a multiply derived class with one int of data and three immediate base classes, the size of each object would actually be 16 bytes (on a 32-bit system; more on 64-bit). 4 for the int and four each for each of the vtable pointers. Plus, of course, the sizes of each of the base classes themselves.

That means that in C++, interfaces are not cheap. (Obviously there are no true interfaces in C++, but a base class with no data and only pure virtual methods emulates it.) Each such interface costs the size of a pointer per object.

In languages like C# and Java, where interfaces are part of the language, there is a slightly different mechanism whereby all interfaces are routed through a single vtable pointer. This is slightly slower, but means only one vtable pointer per object, however many interfaces are implemented.

I'd still follow an interface style approach in C++ for design reasons, but always be aware of this additional overhead.

(And none of this even touches on virtual inheritance.)

Jasper Kent
  • 3,546
  • 15
  • 21
  • I think this makes sense, so basically if I had 3 different derived classes, each class would have Base vtable appended with it's own table and I assume that with each further subclass, that subclass would just append it's own table? – Antonio Antolcic Mar 26 '20 at 22:31
  • For multiple inheritance, there's a few more twists involved. Yet still a compiler implementation detail, and not something that should be exploited in the code (it'd be very precarious code, and not portable). – Eljay Mar 26 '20 at 22:40
  • @Eljay God yes! Multiple inheritance is horrible, though I've added an important caveat to my answer. – Jasper Kent Mar 26 '20 at 22:46
  • 1
    @AntonioAntolcic: related: [How do objects work in x86 at the assembly level?](https://stackoverflow.com/q/33556511) shows some real compiler-generated asm for x86 for some of the simplest cases of accessing data members and making virtual functions. I think you already know that much, but possibly future readers would benefit. Nice summary of inheritance, Jasper, +1 – Peter Cordes Mar 26 '20 at 23:38