While the C++ standard leaves the implementation of virtual dispatch up to the compiler, there are only 3 major compilers (gcc, clang and msvc) at this point.
How do they implement virtual dispatch, when you invoke a method on an abstract base through a pointer to that abstract base? How does the vtable get set up during contruction?
A simple "as if" example would be useful.
Every major C++ implementation uses a vtable. This is a pointer to an array (or struct) of method pointers. There is one vtable for a given type (excluding some corner cases involving dynamic loading).
If you write this:
the compiler creates something quite similar to this:
at the assembly level produced by C++ compilers, an array of packed function pointers and a structure of packed function pointers is identical. Here I'll use a struct so I don't have to mess with casting.
When you inherit from
Iswitch:the compiler generates an output similar to:
When you create the derived object, it sets the vtable pointer in the base object to point to its vtable as part of its constructor.
Then, when you access a method via virtual dispatch (usually just calling it), code looks up the method pointer in the vtable and invokes it.
If you have
the code that is run (in this function) is identical regardless of which derived class of
Iswitchthepswitchpoints at; at least until it gets to the body ofturn_onorturn_off.this does fundamentally the same thing in the
class"compiler does it for me" version ofIswitchandMySwitchas it does in the manualstructversion.dynamic_destroyis a special helper I added. When you delete a C++ object via pointer with a virtual destructor, it looks up the destructor in the vtable and uses it to clean up the object.in the manual
structcase, the above becomes:Here you can see the two versions compiled side by side.
In the real generated code there are a few differences.
In addition, if you inherit using the
virtualthings get more interesting, because now your vtable has pointers to vtables in it instead of just methods.If we extend the
Iswitchinterface, we just get a larger array/vtable struct, where the first part of the extended vtable matches the base class.corresponds to:
and the classes implementing it have to point to a full
Iswitch_extendedtable with thevtable.All of these techniques existed in C code bases prior to C++ being specified. When Bjarn was designing C++, he had this stuff in mind; the idea is that writing this boilerplate was annoying, and having the compiler write it for you was nice.
The downside is that there is more than one way to do all of this. While every major compiler used the above technique, MFC used a switch-based dispatch mechanism for its polymorphism.
The problem with the above vtable approach is that every concrete class needs a O(number of virtual methods) table. In MFC's case, the number of methods that needs to be possibly overridden is large (almost every windows message!). So instead of a huge function table, the message ID is passed to dispatch function, which is daisy chained. If a subclass wants to intercept that message on this object, it stops the chain and returns a function pointer (or executes the message directly on the function pointer).
But, with a fixed generator of dynamic dispatch code built into the language, the alternative approaches are far more expensive, and get neglected, even if they would be better for a specific use case.
I, for one, am looking forward to reflection. With reflection we'll be able to generate the dynamic dispatch code as efficiently as the compiler does, but target a different choice of OO model.