codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Subjects
🧩

OOP Concepts

23 chapters

1Procedural vs Object-Oriented2Classes, Objects, & Instantiation3Constructors & Destructors4Static Members & Methods5Encapsulation & Access Modifiers6Data Abstraction7Inheritance Types (Single, Multiple)8Compile-Time Polymorphism (Overloading)9Polymorphism & Interfaces10Run-Time Polymorphism (Overriding)11Virtual Functions & V-Tables12Interfaces & Abstract Classes13Generic Programming (Templates & Generics)14Exception Handling in OOP15SOLID Design Principles16Composition over Inheritance17Coupling & Cohesion18UML Diagrams Basics19Creational Patterns (Singleton, Factory)20Structural Patterns (Adapter, Decorator)21Behavioral Patterns (Observer, Strategy)22MVC Architecture Pattern23Object Serialization & Cloning
SubjectsOOP Concepts

Virtual Functions & V-Tables

Updated 2026-05-05
2 min read

Virtual Functions & V-Tables

In the previous chapter, we saw that Java supports run-time polymorphism (dynamic dispatch) automatically for all non-static, non-final methods. In C++, however, this behavior must be explicitly requested by the programmer using the virtual keyword.

1. The Problem Without virtual in C++

In C++, if you use a base class pointer to call a method, the compiler uses the declared type of the pointer to decide which function to call (Static Binding), not the actual type of the object it points to.

class Animal {
public:
    void speak() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {
public:
    void speak() { cout << "Woof!" << endl; }
};

Animal* ptr = new Dog();
ptr->speak(); // Output: "Animal sound" (WRONG! We wanted "Woof!")

Because speak() is not declared virtual, the compiler sees that ptr is of type Animal* and statically binds the call to Animal::speak(). It completely ignores the fact that ptr actually points to a Dog object.

2. The virtual Keyword

To enable run-time polymorphism in C++, you must declare the method in the base class as virtual.

class Animal {
public:
    virtual void speak() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {
public:
    void speak() override { cout << "Woof!" << endl; }
};

Animal* ptr = new Dog();
ptr->speak(); // Output: "Woof!" (CORRECT! Dynamic dispatch is now active.)

3. Pure Virtual Functions

A Pure Virtual Function is a virtual function that has no implementation in the base class. It is declared by assigning = 0 to the function declaration. Any class containing at least one pure virtual function automatically becomes an Abstract Class and cannot be instantiated.

class Shape {
public:
    virtual double area() = 0; // Pure virtual: NO implementation here
};

class Circle : public Shape {
public:
    double radius;
    double area() override {
        return 3.14159 * radius * radius;
    }
};

// Shape* s = new Shape();  // ERROR! Shape is abstract.
Shape* s = new Circle();    // OK. Circle provides the implementation.

4. Virtual Destructors

If a base class has virtual functions, its destructor should always be declared virtual. Otherwise, deleting a derived class object through a base class pointer will only call the base destructor, causing a memory leak.

class Base {
public:
    virtual ~Base() { cout << "Base destroyed" << endl; }
};

class Derived : public Base {
public:
    ~Derived() { cout << "Derived destroyed" << endl; }
};

Base* ptr = new Derived();
delete ptr;
// Output: "Derived destroyed" then "Base destroyed" (CORRECT)
// Without virtual: only "Base destroyed" (MEMORY LEAK)

5. How V-Tables Work (Under the Hood)

When you mark a method as virtual, the C++ compiler generates a hidden lookup table called a Virtual Table (V-Table) for each class that has virtual functions.

  1. Each class with virtual functions gets its own V-Table. The V-Table is an array of function pointers, one entry for each virtual method.
  2. Each object of that class gets a hidden pointer called the V-Pointer (vptr) that points to its class's V-Table.
  3. When you call ptr->speak(), the compiled code does NOT jump directly to a hardcoded function address. Instead, it: a. Follows ptr to the object. b. Reads the hidden vptr from the object. c. Uses the vptr to find the correct V-Table. d. Looks up the function pointer for speak() in the V-Table. e. Calls the function at that address.

This extra level of indirection (pointer chasing) is why virtual function calls are slightly slower than regular function calls, but the flexibility they provide is well worth the tiny performance cost.



PreviousRun-Time Polymorphism (Overriding)NextInterfaces & Abstract Classes

Recommended Gear

Run-Time Polymorphism (Overriding)Interfaces & Abstract Classes