C++ Virtual Functions and vtables: How Dynamic Binding Works [#33-1]

C++ Virtual Functions and vtables: How Dynamic Binding Works [#33-1]

이 글의 핵심

Explain polymorphism: Base* to Derived object, vptr to per-class vtable, virtual destructor for delete through base pointer, NVI and factory patterns, and devirtualization with final.

Introduction: answering “what is a virtual function?” in interviews

Stopping at “for overriding” invites follow-ups: how does the right function run at runtime, where do vtables live, what is vptr? This article explains vtable layout, object memory, and dynamic binding so you can describe p->virt() through a Base* pointing at a Derived.


Static vs dynamic binding

  • Non-virtual member calls use the static type of the pointer/reference.
  • Virtual calls use the dynamic type of the object—implemented via vptr → vtable.
Base* p = new Derived();
p->normal();  // Base::normal — not virtual
p->virt();    // Derived::virt — virtual

vtable and vptr

Each polymorphic class has a vtable (list of function pointers + special entries such as RTTI/destructor slots per ABI). Each object stores a vptr to its class’s vtable. Virtual call = load vptr, index slot, indirect jump.

GCC: -fdump-class-hierarchy shows class hierarchy and vtable layouts.


Virtual destructor

Deleting through Base* without a virtual destructor runs only Base::~Baseundefined behavior and leaks derived resources. Rule: polymorphic base ⇒ virtual ~Base().


Common mistakes

  1. Non-virtual destructor on polymorphic base.
  2. Default arguments on virtual overrides—statically bound to the static type’s defaults.
  3. Virtual calls from ctor/dtor—dynamic type not fully Derived.
  4. Missing override—accidental overload hiding.
  5. Object slicing when passing by value.
  6. Exceptions escaping destructors.

override / final

KeywordPurpose
virtualEnable overriding in derived classes
overrideExplicit override; mismatch errors
finalNo further override / inheritance

Multiple inheritance

Each polymorphic subobject may have its own vptr; pointer adjustments when casting between bases.


Pure virtual / abstract classes

= 0 makes a class abstract; concrete derived types must implement all pure virtuals.


Performance

Virtual calls cost indirection and usually no inlining on that path. Devirtualization possible when the concrete type is provably fixed (e.g. final, LTO). Prefer measurement over blanket avoidance of virtual.


Interview answers (short)

  • Virtual function: mechanism for dynamic binding through base pointers/references.
  • vtable: per-class table of addresses for virtual/overrides and related slots.
  • vptr: per-object pointer to the vtable for the object’s dynamic type.

  • VTable guide
  • Virtual functions
  • Inheritance and polymorphism

Next: Copy vs move #33-2

Previous: STL cheatsheet #32-3