Virtual and Pure Virtual Functions in C++

Virtual and Pure Virtual Functions in C++

In our previous lessons, we explored how inheritance allows one class to reuse and extend the features of another. We also learned about function overriding, where a derived class can redefine the behavior of a base class function.

But there’s a deeper question that often puzzles students:

If a base class pointer points to a derived class object, which version of the function will be called; the one from the base or the one from the derived class?

This question brings us to an important concept called Virtual Functions, one of the most powerful features of polymorphism in C++.

A simple way to define it is:

A virtual function is a function in a base class that can be redefined in derived classes, and its call is resolved at runtime rather than compile time.

In simpler terms, C++ waits until the program is running to decide which function version to call; depending on the type of object the pointer is actually pointing to.

Real-Life Example: Employees and Their Duties

Imagine a company that hires many types of employees — teachers, engineers, and managers. Each of them performs a different duty, but the company only knows that everyone must perform duty.

The company management doesn’t need to know the exact details of what each person does. It just asks the employee to perform the duty, and the employee responds according to their role.

A teacher will teach, an engineer will design, and a manager will manage.

This simple relationship reflects what virtual functions do in programming. The base class represents the company rule, and the derived classes represent the employees who perform different duties. When the company calls performDuty(), the correct action is performed depending on the actual employee type.

Understanding Virtual Functions Through Code

Let’s now bring this idea into C++ with an example based on our company example.

#include <iostream>
using namespace std;

// Base class representing a generic employee
class Employee {
public:
    // Declaring a virtual function
    virtual void performDuty() {
        cout << "Performing a general company duty." << endl;
    }
};

// Derived class representing a Teacher
class Teacher : public Employee {
public:
    void performDuty() override {
        cout << "Teaching students and preparing lessons." << endl;
    }
};

// Derived class representing an Engineer
class Engineer : public Employee {
public:
    void performDuty() override {
        cout << "Designing systems and writing code." << endl;
    }
};

int main() {
    Employee* staff;  // Base class pointer

    Teacher t;
    Engineer e;

    staff = &t;
    staff->performDuty();   // Calls Teacher’s version

    staff = &e;
    staff->performDuty();   // Calls Engineer’s version

    return 0;
}

In this program, the base class Employee declares the function performDuty() as virtual.
Both Teacher and Engineer redefine this function in their own way.

When we use the base class pointer staff to point to a Teacher object, it calls the teacher’s version. When we point it to the engineer, it calls the engineer’s version.

This is called runtime polymorphism because the exact version of the function is decided at runtime, depending on the actual object being pointed to.

If we had not used the virtual keyword, the compiler would have always called the base class version, ignoring the derived ones. That would be static binding. By declaring the function virtual, we enable dynamic binding; the true essence of polymorphism.

Moving Forward: The Need for Pure Virtual Functions

Sometimes, we don’t even want the base class to provide any implementation. We just want it to declare what must be done, leaving it to the derived classes to decide how it’s done.

This situation is like a company rulebook that defines the responsibilities of all employees but doesn’t describe how to perform them. Every employee must follow the rule and perform their specific duty.

The rulebook is the abstract base class, and the rule itself — the declaration — is the pure virtual function.

Code Example for Pure Virtual Function

#include <iostream>
using namespace std;

// Abstract base class (acts like a rulebook)
class Employee {
public:
    // Pure virtual function - notice the = 0
    virtual void performDuty() = 0;
};

// Derived class: Teacher
class Teacher : public Employee {
public:
    void performDuty() override {
        cout << "Teaching students and preparing lessons." << endl;
    }
};

// Derived class: Engineer
class Engineer : public Employee {
public:
    void performDuty() override {
        cout << "Designing systems and writing code." << endl;
    }
};

int main() {
    // We cannot create an object of Employee (abstract class)
    // Employee e;   // ❌ This will cause a compilation error

    Employee* staff;  // Base class pointer

    Teacher t;
    Engineer e1;

    staff = &t;
    staff->performDuty();   // Calls Teacher’s implementation

    staff = &e1;
    staff->performDuty();   // Calls Engineer’s implementation

    return 0;
}

In this example, performDuty() is declared as a pure virtual function using = 0.
This means the base class Employee defines what every employee must do but doesn’t explain how to do it. Each derived class provides its own version of the function.

A class that contains even one pure virtual function becomes an abstract class, meaning you cannot create its objects. Instead, it serves as a blueprint for other classes.

If a derived class fails to implement the pure virtual function, it too becomes abstract and cannot create objects.

This mechanism helps maintain consistency and ensures that all derived classes follow the same structure but with their own unique behavior.

Connecting Back to the Concept

A virtual function gives flexibility. It allows a base class to provide a default version that can be overridden when necessary.

A pure virtual function enforces a rule; it doesn’t define any version and leaves the implementation entirely to derived classes.

Both together form the foundation of polymorphism in C++: virtual functions enable runtime decisions, and pure virtual functions provide structured abstraction.

In this way, C++ beautifully balances freedom and discipline — allowing you to create systems that are flexible, scalable, and logically consistent.

Practice Task for You

Design a base class called PaymentMethod that declares a pure virtual function named makePayment(). Then, create two derived classes — CashPayment and OnlinePayment.

Each class should define its own version of makePayment() describing how the payment is processed. Finally, use a base class pointer to call both versions dynamically.

By completing this, you will experience how abstraction and runtime polymorphism combine to make your programs cleaner and more powerful.

Leave a Reply

Your email address will not be published. Required fields are marked *