In C++, how does a call through a base-class pointer reach the correct derived-class function at run time? Explain the vtable/vptr mechanism, its costs, and why a base class with virtual functions should usually have a virtual destructor.
Show hints
Each polymorphic object carries one hidden pointer.
Show the explanation
Model answer. The C++ standard only specifies the behaviour of virtual functions; virtually every compiler implements it with virtual tables.
The mechanism
- For each class that has virtual functions, the compiler creates a vtable: a table of function pointers, one slot per virtual function, pointing to the implementation that class uses (its own override or the inherited one).
- Each object of such a class contains a hidden pointer, the vptr, set by the constructor to point to its class's vtable.
- A call
basePtr->draw()compiles roughly to: load the vptr from the object, index the slot fordraw, and call through that function pointer. Because aCircleobject's vptr points toCircle's vtable,Circle::drawruns even though the pointer's static type isShape*. - Non-virtual functions are bound at compile time and do not use the vtable.
Costs
- Memory: one vptr per object (8 bytes on typical 64-bit systems) plus one vtable per class.
- Time: an extra indirect load and indirect call. More significant is that the compiler usually cannot inline virtual calls unless it can prove the dynamic type (devirtualisation, helped by
final).
Virtual destructors
Shape* s = new Circle();
delete s;If Shape::~Shape() is not virtual, deleting a derived object through a base pointer is undefined behaviour — in practice Circle's destructor typically does not run, leaking whatever it owned. Declaring virtual ~Shape() = default; makes destruction dispatch through the vtable, so ~Circle() runs first, then ~Shape().
Subtle point: virtual calls in constructors and destructors
While Shape's constructor runs, the object is still only a Shape (the vptr points to Shape's vtable), so a virtual call inside it invokes Shape's version, not Circle's. Calling a pure virtual function this way is undefined behaviour.
Follow-ups an interviewer may ask
- "What does
sizeofreturn for an empty class vs an empty class with one virtual function?" Typically 1 vs 8 on a 64-bit compiler (implementation-defined). - "How does Java compare?" All non-static, non-private, non-final Java methods are virtual by default; the JVM uses similar method tables, and the JIT aggressively inlines monomorphic calls.
Original UniCircle question. Last reviewed 28 Oct 2026.
