In C++, what’s a vtable and how does it work?
Vtables: Known by Many Different Names
It’s worth a few brain cells to remember that a vtable is known by many different names: virtual function table, virtual method table, and even as a dispatch table. When interviewing, it’s always a good idea to be familiar with the terminology.
Vtables: Used Behind the Scenes of Polymorphism
When working with virtual functions in C++, it’s the vtable that’s being used behind the scenes to help achieve polymorphism. And, although you can understand polymorphism without understanding vtables, many interviewers like to ask this question just to see if you really know your stuff.
Vtables contain pointers to virtual functions
Whenever a class itself contains virtual functions or overrides virtual functions from a parent class the compiler builds a vtable for that class. This means that not all classes have a vtable created for them by the compiler. The vtable contains function pointers that point to the virtual functions in that class. There can only be one vtable per class, and all objects of the same class will share the same vtable.
Vpointers point to the vtable
Associated with every vtable is what’s called a vpointer. The vpointer points to the vtable, and is used to access the functions inside the vtable. The vtable would be useless without a vpointer.
For any class that contains a vtable, the compiler will also add "hidden" code to the constructor of that class to initialize the vpointers of its objects to the address of the corresponding vtable.
Because you’re probably confused now, let’s take a look at an example so that we can explain vtables more clearly and in more detail. Take a look at the code below:
Only one vtable per class
The vtable contains function pointers that point to the virtual functions in that class. It’s important to note that there can only be one vtable per class, and all objects of the same class will share the same vtable. This means that in the example above, the Animal and Tiger classes will each have their very own vtable, and any objects of the Animal or Tiger classes will use their respective class’s vtables.
Vtables are used at runtime
In the example above, because the Tiger class overrides the getWeight virtual function provided in the Animal class, the compiler will construct a vtable for the Tiger class. This vtable will only contain a function pointer for the getWeight function. The getHeight function will not be put inside the vtable because it is not virtual, nor does it override a virtual function in the Animal base class.
The question that the code above raises is at runtime how does the call "a1 -> getWeight()" know to use the version of getWeight provided in the Tiger – and not the Animal class. The answer, as you probably guessed, is by using vtables.
We now know that a vtable will be created for the Tiger class. And every vtable must have a vpointer that points to the vtable (otherwise the vtable can not be referenced). Let’s call the vpointer that belongs to the Tiger class vptr1 – this is just a name we created so we can show our point.
Take a look at the code below again.
Because the Tiger class contains a pointer to the vtable called vptr1, the call "a1 -> getWeight()" will actually be translated to "(*(a1 -> vptr1 -> getWeight())".
This means that the definition of getWeight() provided in the Tiger class will be called, which is what we want.
What if vtables were not used?
Now that we’ve seen vtables in action we should ask ourselves again why C++ would use vtables? Hopefully at this point you can answer that question yourself. But, just to review, vtables are used so that the correct definition of the function can be called at runtime – basically to help achieve polymorphism. In our example, we wanted the definition of the getWeight() function provided in the Tiger class to be called. And that is exactly what will happen with the help of vtables.