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.

Subscribe to our newsletter for more free interview questions.

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:

class Animal // base class
{

public:

int weight;

virtual int getWeight() {};
}


// Obviously, Tiger derives from the Animal class
class Tiger: public Animal {

public:

int weight;

int height; 

int getWeight() {return weight;};

int getHeight() {return height;};

int main()
{	
	Tiger t1;
	
	/* below, an Animal object pointer is set to point
	   to an object of the derived Tiger class  */
	
	Animal *a1 = &t1; 
	
	/*  below, how does this know to call the 
		definition of getWeight in the Tiger class, 
		and not the definition provided in the Animal 
		class  */
		
	a1 -> getWeight(); 
}			
}

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.

// Obviously, Tiger derives from the Animal class
class Tiger: public Animal {

/*
...some code left out here

*/

int main()
{	
	Tiger t1;
	
	/* below, an Animal object pointer is set to point
	   to an object of the derived Tiger class  */
	
	Animal *a1 = &t1; 
	
	/*  below, how does this know to call the 
		definition of getWeight in the Tiger class, 
		and not the definition provided in the Animal 
		class  */
		
	a1 -> getWeight(); 
}			
}	

Because the Tiger class contains a pointer to the vtable called vptr1, the call "a1 -> getWeight()" will actually be translated to "(*(a1 -> vptr1 -> getWeight())".

Tiger t1;
	
/* below, an Animal object pointer is set to point
to an object of the derived Tiger class  */
	
Animal *a1 = &t1; 						
a1 -> getWeight(); 

/* 
   The call above gets translated to this,
   assuming the pointer to the vtable for the
   Tiger class is called vptr1
   :  
   *(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.

Hiring? Job Hunting? Post a JOB or your RESUME on our JOB BOARD >>

Subscribe to our newsletter for more free interview questions.

  • Najib Ghadri

    Actually * is not at all necessary. I think you know that both in c and cpp function pointers can be derefd with or without *. Right?

  • rohit chaudhari

    what will be the size of the each class object
    Its Showing 8 bytes for animal class and 16 bytes for tiger and lion class
    and 28 bytes for liger class ….. plz explain for size…thank you

  • orangetinyterrors

    And class definitions need a trailing ; too.

  • Ashwin R S

    In *(a1 -> vptr1 -> getWeight()) As I understand vptr1 points to a vtable. And that vtable has a list of function pointers which are virtual. How does the compiler know which entry in the vtable corresponds to getWeight()?

  • nom

    А есть еще что то похожее на эту тему?

  • Lars Olav Tolo

    It is because you want to use the function that the function pointer in the vtable points to rather than trying to run the function pointer as a function. The function pointer in the vtable would be
    int (*getWeight)() = &(Tiger::getWeight);
    So you would access this pointer by
    vptr->getWeight
    And you would have to dereference the pointer to use the function.
    *(vptr->getWeight)();

  • pinglei

    watch below codes for better understanding vtable:

    #include

    using namespace std;

    class Base1 {

    public:

    virtual void f() { cout << "Base1::f" << endl; }

    virtual void g() { cout << "Base1::g" << endl; }

    virtual void h() { cout << "Base1::h" << endl; }

    };

    class Base2 {

    public:

    virtual void f() { cout << "Base2::f" << endl; }

    virtual void g() { cout << "Base2::g" << endl; }

    virtual void h() { cout << "Base2::h" << endl; }

    };

    class Base3 {

    public:

    virtual void f() { cout << "Base3::f" << endl; }

    virtual void g() { cout << "Base3::g" << endl; }

    virtual void h() { cout << "Base3::h" << endl; }

    };

    class Derive : public Base1, public Base2, public Base3 {

    public:

    virtual void f() { cout << "Derive::f" << endl; }

    virtual void g1() { cout << "Derive::g1" << endl; }

    };

    typedef void(*Fun)(void);

    int main()

    {

    Fun pFun = NULL;

    Derive d;

    int** pVtab = (int**)&d;

    //Base1's vtable

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);

    pFun = (Fun)pVtab[0][0];

    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);

    pFun = (Fun)pVtab[0][1];

    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);

    pFun = (Fun)pVtab[0][2];

    pFun();

    //Derive's vtable

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);

    pFun = (Fun)pVtab[0][3];

    pFun();

    //The tail of the vtable

    pFun = (Fun)pVtab[0][4];

    cout<<pFun<<endl;

    //Base2's vtable

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);

    pFun = (Fun)pVtab[1][0];

    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);

    pFun = (Fun)pVtab[1][1];

    pFun();

    pFun = (Fun)pVtab[1][2];

    pFun();

    //The tail of the vtable

    pFun = (Fun)pVtab[1][3];

    cout<<pFun<<endl;

    //Base3's vtable

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);

    pFun = (Fun)pVtab[2][0];

    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);

    pFun = (Fun)pVtab[2][1];

    pFun();

    pFun = (Fun)pVtab[2][2];

    pFun();

    //The tail of the vtable

    pFun = (Fun)pVtab[2][3];

    cout<<pFun<<endl;

    return 0;

    }

  • alex

    vptr is created when you create the particular instance… so vptr is different for different derived classes.

  • srinivas

    If vptr is inherited from base to two different derived classes with different funtion implementation… then how is it ensured that we call the correct function for that instance ?

  • srinivas

    can you please explain why do we need a ” * ” at all ?

  • stri8ed

    Imagine a function printMeasurments(Animal a). At run-time, how does it know which getweight() method to call?

  • doll

    Hi, why would we declare Animal a1* = &t if we want to call getweight of Tiger class? We can directly use Tiger *t; and t->getweight()?

  • Roger

    One more thing to add, is that the vptr is only added in the base class. Then, the vptr is inherited in the derived class. Like this, when you have the pointer a1 (that is a pointer to the base class), you can access the vptr because it is declared in the base class. (An Animal pointer can only point to the Animal part of the object, even if it is actually a Tiger object)

  • Jan

    Nice article. Thanks. Looks like you are a Java programmer.. you put main within the class.

  • pi

    Classes can have more than one vtable for multiple inheritance. http://www-plan.cs.colorado.edu/diwan/class-papers/mi.pdf

  • Алексей Кузнецов

    mistaken in " *(a1 -> vptr1 -> getWeight()) ", right is " *(a1 -> vptr1 -> getWeight ) () "

  • tristan

    thak you

  • aparna vegendla

    thank you