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.