In your example, the depth of the inheritance tree does not affect performance. The reason is simple, every instance has its pointer to some vtable (containing function pointers), and a virtual function call goes just thru that vtable.
In other words, your code works like the following C code:
struct vtable {
void (*fooptr) (struct D1*);
};
struct D1 {
const struct vtable*vptr;
};
struct D2 {
const struct vtable*vptr;
};
And e.g. baseptr->foo()
is "transformed" at compilation time to baseptr->vptr.fooptr(baseptr)
(and of course d1ptr->foo()
is "transformed" to d1ptr->vptr.fooptr(d1ptr)
etc...)
So the execution cost is the same: get the vtable, and call indirectly the function pointer inside. The cost remains the same even if you had a struct D4
subclass of struct D3
subclass of struct D2
subclass of struct D1
. You could have a struct D99
with 99 inheritances and the execution cost would remain the same.
On some processors, any indirect call may be slower than a direct call, e.g. because of branch predictor cost.
The data of the vtable themselves is built at compilation time (as constant static data).
Things become a bit more complex with virtual inheritance and/or multiple inheritance
BTW, some C object frameworks (e.g. GObject from Gtk, or several data structures inside the Linux kernel) also provide their vtable (or class data). Pointing to a data structure containing function pointers is quite common (even in C). Read also about closures and you'll see a relation with "vtables" (both closures and objects are mixing code with data).
Some C++ compilers (e.g. GCC) may provide devirtualization as an optimization technique.