Search code examples
c++c++11virtual-functionsvtable

Function resolution from vtable in C++


I have a confusion regarding vtable after reading more about name mangling. for ex:

class Base
{
public:
    virtual void print()
    {
    }
};

class A : public Base
{
public:
    void hello()
    {
        ....
    }

    void print()
    {
    }
};

A obj;
obj.hello();

Base* test = new A();
test->print();

As per my understanding after the name manging obj.hello() call will be converted to something like _ZASDhellov(&obj) now

  1. how this virtual functions will be invoked from vtable?
  2. my wild guess test->__vtable[_ZASDprintv](&test(dynamic cast to derived???)) is correct?
  3. How the function names are resolved from vtable?

Solution

  • Firstly, vtables are not in any way part of the C++ language, but rather an implementation detail used by particular compilers. Below I describe one way it is commonly used as such.

    Second, your function hello is not virtual. To make it virtual, you would simply pre-pend virtual to the declaration.

    Assuming it is now virtual: Your guess is quite close. In fact, the vtable (to which a pointer is stored with every instance of a virtual class) is an array of function pointers. The way that a particular function is looked up in it is by its ordinal. The first declared virtual function in A is the first entry in its vtable, the second one is the second entry and so on. If A had a base class, the index of A's first (non-override) virtual function in the table would be n+1, where n is the index of the last virtual function of its base class. If A has more than one base class, their entries precede A's entries in order of their declaration as base classes of A.

    If A uses virtual inheritance, the picture is a bit more complicated than that, I won't elaborate unless you're specifically interested.

    UPDATE: I'll add a very brief description for the virtual inheritance case as requested. If A had Base as a virtual base class, A's vtable would store at the very beginning (before the function addresses) the byte offset of where Base's data starts within the A object. This is necessary because, unlike in normal inheritance, a base class does not have its data precede the derived class's data - instead it follows it. So in effect, any function call to a virtual function defined in Base has to have its this pointer offset by that amount. Additionally, Base would have to have its own vtable pointer, right at the beginning of its data where it expects to find it. Thus the full A object would contain two vtable pointers instead of one. The actual vtable pointed to by this second pointer would be the same one as the first vtable pointer, except advanced to skip the offset entry described above (so that any Base code using the vtable would find the first virtual function at the beginning where it is expected). Apart from these differences, the vtable itself is the same as before.