I am considering using a class hierarchy with more than a single level of inheritance, where virtual member functions form a "chain", for example:
struct Base
{ virtual void foo(); };
struct D1 : Base
{ virtual void foo(); };
struct D2 : D1
{ void foo(); };
However I would like to know before using similar code in practice if this would cause additional dynamic dispatch overhead when calling trough a base class pointer. Consider the following example:
D2 instance;
D1* d1ptr = &instance;
Base* baseptr = &instance;
//normal function call to "D2::foo()", no dynamic dispatch.
instance.foo();
//virtual function call to "D2::foo()", one dynamic dispatch.
d1ptr.foo();
//virtual function call to "D2::foo()", does this incur additional
//..overhead compared to "d1ptr.foo()"?
baseptr.foo();
Best Answer
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:
And e.g.
baseptr->foo()
is "transformed" at compilation time tobaseptr->vptr.fooptr(baseptr)
(and of coursed1ptr->foo()
is "transformed" tod1ptr->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 ofstruct D3
subclass ofstruct D2
subclass ofstruct D1
. You could have astruct 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.