1

In C++, there is no class representation at run-time but I can always call an overridden virtual method in the derived class. where is that overridden method saved in the vtable? here's a piece of code to demonstrate:

struct B1 {
 virtual void f() { ... }
};
struct B2 {
 virtual void f() { ... }
 virtual void g() { ... }
};
struct D : B1, B2 {
 void f() { ... }
 virtual void h() { ... }
};

What's the memory layout for an object of class D ? Where are B1::f and B2::f saved in that memory layout (if they're saved at all) ?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Loay
  • 483
  • 3
  • 18
  • 3
    That's Compiler specific. c++ standard doesn't imply the concept of a vtable. – πάντα ῥεῖ Sep 13 '15 at 10:40
  • @πάνταῥεῖ thanks for the fast reply! But don't all compilers follow a general standard ? – Loay Sep 13 '15 at 10:44
  • Yes they do, but that standard doesn't imply an implementation needs to create a vtable to realize virtual polymorphism. – πάντα ῥεῖ Sep 13 '15 at 10:46
  • 1
    The memory layout for an *object* of type D is a completely separate thing from the memory layout for the *vtable* for D. – Steve Jessop Sep 13 '15 at 10:48
  • @πάνταῥεῖ I'm guessing you and the OP have different definitions of "standard". – JorenHeit Sep 13 '15 at 10:48
  • I voted to re-open this question because the alleged duplicate asks a distinctly different question. Although OP might find an answer to his question by reading carefully through some of the answers, his question is different from the duplicate. – Sergey Kalinichenko Sep 13 '15 at 10:49
  • @πάνταῥεῖ Yes, but every compiler in existence uses vtables. – curiousguy Sep 13 '15 at 22:20
  • @curiousguy Do you have finally provable evidence for this? It was already mentioned, that compilers can bypass a vtable implementation for reasons of compile time statements deduction. – πάντα ῥεῖ Sep 13 '15 at 22:24
  • @πάνταῥεῖ Obviously when the real type of an object is known, a call to a virtual function need not use the vtable. And various special cases can be optimised, f.ex. an automatic object whose address doesn't escape the function can be optimised aggressively. But the general case uses vtables. – curiousguy Sep 13 '15 at 22:27

3 Answers3

0

An object d of Class D will have only a pointer to the VMT of class D, which will contain a pointer to D::f. Since B1:f and B2::f can be called only statically from the scope of D class, there is no need for object d to keep a dynamic pointer to those overridden methods.

This of cause is not defined in the standard, this is just the usual/logical implementation of the compiler.

In fact the picture is more complicated, since the VMT of class D incorporates the VMTs of classes B1 and B2. But anyway, there is no need to dynamically call B1::f until an object of class B1 is created.

Andrej Adamenko
  • 1,650
  • 15
  • 31
0

When compiler uses vtable method of virtual dispatch*, the address of the overriden member function is stored in the vtable of the base class in which the function is defined.

Each class has access to vtables of all of its base classes. These vtables are stored outside of the memory layout for the class itself. Each class with virtual member functions, declared or inherited, has a single pointer to its own vtable. When you call an overriden member function, you supply the name of the base class whose member function you wish to call. The compiler knows about vtables of all classes, to it knows how to locate the vtable of your base class, does the lookup at compile time, and calls the member function directly.

Here is a short example:

struct A {
    virtual void foo()   { cout << "A"; }
};
struct B : public A { }; // No overrides
struct C : public B {
    virtual void foo()   { cout << "C"; }
    void bar()           { B::foo(); }
};

Demo.

In the example above the compiler needs to look up B::foo, which is not defined in class B. The compiler consults its symbol table to find out that B::foo is implemented in A, and generates the call to A::foo inside C::bar.

* vtables is not the only method of implementing virtual dispatch. C++ standard does not require vtables to be used.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    If code in the class `D` calls `B1::f` then there isn't really any need to virtually dispatch that, is there? So a (typical) compiler won't use the vtable of `B1` to call it, it'll just emit a fixup to the address of the code for `B1::f`. If `D`'s inheritance from `B1` were virtual, though, then there would be an extra step, which is that some run-time work would be needed to figure out how to cast the `D*` `this` to `B1*` to pass in to that call. – Steve Jessop Sep 13 '15 at 10:51
  • @SteveJessop You are right, even when the function is virtual (e.g. in [this example](http://ideone.com/sdlBFG)), the compiler can do the lookup at compile time, and avoid a virtual call altogether. – Sergey Kalinichenko Sep 13 '15 at 10:59
  • @dasblinkenlight I didn't know that, I'm new to OOP and I thought all compilers have to use vtable method of virtual dispatch. thank you! – Loay Sep 13 '15 at 10:59
  • @Loay Compilers can use any code generation method that produces the standard behaviour. Whenever the real (dynamic) type of an object is known, a virtual call doesn't need to reference the vtable. – curiousguy Sep 13 '15 at 22:34
  • @SteveJessop The code in member function of `D` cannot assume that the real type of `*this` is `D`. – curiousguy Sep 13 '15 at 22:37
  • @curiousguy: true, but so what if the dynamic type of the complete object isn't `D`? A call to `B1::f` is still dispatched to the base class implementation, and the type of `this` in a member function of `D` is still `D*` (or of course `const D*` in a `const` member function). – Steve Jessop Sep 13 '15 at 22:43
  • @SteveJessop Of course, if you use the qualified syntax `B1::f()`, the call is done without consideration to the dynamic type. – curiousguy Sep 13 '15 at 22:57
0

Although nothing is mandated in the C++ standard, every known C++ implementation uses the same approach: every class with at least a virtual function has a vptr (pointer to vtable).

You didn't mention virtual inheritance which is a different, more subtle inheritance relation; non-virtual inheritance is a simple exclusive relation between a base class subobject and a derived class. I will assume all inheritance relations are not virtual in this answer.

Here I assume we derive from classes with at least a virtual function.

In case of single inheritance, the vptr from the base class is reused. (Not reusing it just wastes space and run time.) The base class is called "primary base class".

In case of multiple inheritance, the layout of the derived class contains the layout of every base class, just like the layout of a struct in C contains the layout of every member. The layout of D is B1 then B2 (in any order actually, but the source code order is usually kept).

The first class is the primary base class: in D the vptr from B1 points to a complete vtable for D, the vtable with all the virtual functions of D. Each vptr from a non-primary base class points to a secondary vtable of D: a vtable with only the virtual functions from this secondary base class.

The constructor of D must initialize every vptr of the class instance to point to the appropriate vtable of D.

curiousguy
  • 8,038
  • 2
  • 40
  • 58