Skip to main content
C++ Fundamentals for Beginners to Advanced
CHAPTER 18 Beginner

Polymorphism and Virtual Functions

Updated: May 17, 2026
5 min read

# CHAPTER 18

Polymorphism and Virtual Functions

1. Introduction

Polymorphism comes from Greek, meaning "many forms." In programming, it allows us to perform a single action in different ways. If you have an array of Animal pointers, and one points to a Dog and another to a Cat, polymorphism ensures that calling speak() on both will result in a bark and a meow, respectively.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand Compile-time vs. Runtime Polymorphism.
  • Override Base class methods in Derived classes.
  • Use the virtual keyword to enable Runtime Polymorphism.
  • Understand Virtual Destructors.
  • Create Pure Virtual Functions (Abstract classes).

3. Types of Polymorphism

  1. 1. Compile-Time (Early Binding): The compiler knows exactly which function to call when it compiles the code. Achieved via Function Overloading and Operator Overloading.
  1. 2. Run-Time (Late Binding): The program decides which function to call *while it is running* based on the actual object type. Achieved via Virtual Functions.

4. The Problem: Function Overriding without virtual

When a derived class creates a function with the *exact same name* as a function in the base class, it overrides it. But look what happens when we use pointers:
cpp
1234567891011121314151617181920
#include <iostream>
using namespace std;

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

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

int main() {
    Animal* ptr = new Dog(); // Base pointer pointing to a Derived object!
    
    ptr->speak(); // Output: "Animal sound" (Wait, it didn't say Woof!)
    
    return 0;
}

Why did this happen? Because ptr is of type Animal*, the compiler defaults to the Animal's version of speak() at compile-time (Early Binding).

5. The Solution: Virtual Functions

To fix this, we add the virtual keyword to the base class function. This tells the compiler: "Wait until runtime to see what this pointer is *actually* pointing at, and call that specific version of the function" (Late Binding).
cpp
12345678910111213141516171819
class Animal {
  public:
    // virtual keyword enables runtime polymorphism
    virtual void speak() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {
  public:
    // Use the 'override' keyword (C++11) for safety and readability
    void speak() override { cout << "Woof!" << endl; }
};

int main() {
    Animal* ptr = new Dog(); 
    
    ptr->speak(); // Output: "Woof!" (It works!)
    
    return 0;
}

6. The Virtual Destructor (CRITICAL)

If you delete a derived object through a base pointer, and the base class does NOT have a virtual destructor, only the Base part of the object is destroyed. The Derived part is left in memory, causing a massive memory leak!

Rule of Thumb: If your class has ANY virtual functions, it MUST have a virtual destructor.

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

class Derived : public Base {
  public:
    ~Derived() { cout << "Derived destroyed"; }
};
// Now, 'delete ptr;' will correctly call Derived destructor, then Base destructor.

7. Pure Virtual Functions and Abstract Classes

Sometimes, a Base class is just a concept, and you don't want anyone to create an object of it. (e.g., You shouldn't be able to instantiate a generic Shape, only a Circle or Square).

By assigning = 0 to a virtual function, you make it a Pure Virtual Function. Any class with at least one pure virtual function becomes an Abstract Class, meaning it cannot be instantiated.

cpp
123456789101112131415161718
class Shape {
  public:
    // Pure Virtual Function
    virtual void draw() = 0; 
};

class Circle : public Shape {
  public:
    // Derived class MUST implement this function!
    void draw() override { cout << "Drawing a Circle" << endl; }
};

int main() {
    // Shape s; // ERROR! Cannot instantiate abstract class
    Shape* s1 = new Circle();
    s1->draw();
    return 0;
}

8. Memory-Level Explanation (The VTable)

How does C++ know which function to call at runtime? When a class has virtual functions, the compiler creates a Virtual Table (VTable). It is an array of function pointers. Every object of that class gets a hidden pointer (the vptr) pointing to this table. At runtime, the program looks up the VTable to find the correct speak() or draw() function address. This is why virtual functions are slightly slower than regular functions.

9. Common Mistakes

  • Forgetting virtual on the Destructor: Causes partial deletion of objects and memory leaks.
  • Signature Mismatch: If the Base class has virtual void speak() const and the Derived class has void speak(), they are DIFFERENT functions. The override keyword catches these errors at compile time.

10. Exercises

  1. 1. Create an abstract class Vehicle with a pure virtual function move(). Create Car and Boat classes that implement move().
  1. 2. Write a program with an array of Vehicle* pointers. Put a Car and a Boat in the array, and loop through them, calling move() on each.

11. MCQ Quiz with Answers

Question 1

What does Polymorphism mean?

Question 2

Function overloading is an example of:

Question 3

Which keyword is required for run-time polymorphism in C++?

Question 4

What is Late Binding?

Question 5

What is the correct syntax for a pure virtual function?

Q6. Can you instantiate an Abstract Class? a) Yes b) No Answer: b) No
Question 7

If a Base class pointer points to a Derived object, and speak() is NOT virtual, which version runs?

Question 8

What feature does C++ use behind the scenes to resolve virtual function calls?

Question 9

Why is a virtual destructor important?

Question 10

What does the override keyword do in C++11?

12. Interview Questions

  • Q: Explain how the VTable and vptr work under the hood.
  • Q: Can a constructor be virtual? Why or why not? (Answer: No, because the vptr isn't fully initialized until the constructor finishes).
  • Q: What is an Abstract Class? Can it have non-virtual functions? (Answer: Yes, it only needs *one* pure virtual function to become abstract).

13. Summary

Polymorphism allows a single interface to control a variety of different objects. By using virtual functions, C++ waits until runtime to execute the correct derived method (Late Binding). Pure virtual functions create Abstract classes, forcing derived classes to implement specific behaviors.

14. Next Chapter Recommendation

In Chapter 19: Encapsulation and Abstraction, we will solidify our understanding of how to protect our objects' internal data using getters, setters, and interfaces.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·