Open In App

Virtual Function in C++

Last Updated : 30 Sep, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

A virtual function is a member function that is declared within a base class using the keyword virtual and is re-defined (Overridden) in the derived class. Virtual functions enable runtime polymorphism, calling the correct function via a base class pointer or reference.

  • Virtual functions are mainly related to inheritance, allowing derived classes to provide their own implementation of base class functions.
  • When a derived class object is deleted through a base class pointer, a virtual destructor in the base class ensures that both the derived and base class destructors are called, safely releasing all resources.
  • The virtual destructor is declared using the keyword virtual in the base class.
  • If a class has any virtual functions, its destructor should also be declared virtual. This is crucial for proper cleanup in polymorphic scenarios, especially when dealing with dynamic memory and inheritance.
C++
#include <iostream>
using namespace std;

class Shape
{
  public:
  
    // Virtual function
    virtual void calculate()
    {
        cout << "Area of your Shape ";
    }

    // Virtual destructor
    virtual ~Shape()
    {
        cout << "Shape Destructor called\n";
    }
};

// Derived class: Rectangle
class Rectangle : public Shape
{
  public:
    int width, height, area;

    void calculate() override
    {
        width = 5;
        height = 10;

        area = height * width;
        cout << "Area of Rectangle: " << area << "\n";
    }

    ~Rectangle()
    {
        cout << "Rectangle Destructor called\n";
    }
};

// Derived class: Square
class Square : public Shape
{
  public:
    int side, area;

    void calculate() override
    {
        side = 7;
        area = side * side;
        cout << "Area of Square: " << area << "\n";
    }

    ~Square()
    {
        cout << "Square Destructor called\n";
    }
};

int main()
{

    Shape *S;

    Rectangle r;
    S = &r;
    S->calculate();

    Square sq;
    S = &sq;
    S->calculate();

    return 0;
}

Output
Area of Rectangle: 50
Area of Square: 49
Square Destructor called
Shape Destructor called
Rectangle Destructor called
Shape Destructor called

Note: It is a recommended way to use override identifier to avoid mistakes while redefine virtual function inside the derived class.

Pure Virtual Function

A pure virtual function is a function in a base class with = 0 and no body, which must be overridden in derived classes. A class with such a function is called abstract and cannot be instantiated.
A pure virtual destructor is a destructor in a base class declared with = 0. It makes the class abstract and ensures that when you delete a derived class object through a base class pointer, the derived destructor runs first, followed by the base destructor, cleaning up everything properly.

C++
#include <iostream>
using namespace std;

class Base
{
  public:
  
    // Pure virtual function
    virtual void display() = 0;

    // Pure virtual destructor
    virtual ~Base() = 0;
};

// Definition of pure virtual destructor
Base::~Base()
{
    cout << "Base destructor called" << endl;
}

class Derived : public Base
{
  public:
    void display() override
    {
        cout << "Derived class display" << endl;
    }

    ~Derived()
    {
        cout << "Derived destructor called" << endl;
    }
};

int main()
{
    Base *basePtr;
    Derived derivedObj;
    basePtr = &derivedObj;
    basePtr->display();
    return 0;
}

Output
Derived class display

Early Binding and Late Binding

When a function is called in the code, binding decides which function gets executed based on the context such as the type of object or the function signature. Binding happens at two levels:

  • Early Binding: It happens when a function call is resolved during the program's compilation. This makes it faster because everything is decided early
  • Late Binding: It happens with virtual functions where the exact function to call is decided at runtime, depending on the actual object type. This is slower because the program has to figure it out while running.
C++
#include <iostream>
using namespace std;

class base
{
  public:
    virtual void print()
    {
        cout << "print base "
                "class\n";
    }

    void show()
    {
        cout << "show base class\n";
    }
};

class derived : public base
{
  public:
    void print()
    {
        cout << "print derived class\n";
    }

    void show()
    {
        cout << "show derived class\n";
    }
};

int main()
{
    base *bptr;
    derived d;
    bptr = &d;

    // Virtual function,binded at runtime
    bptr->print();

    // Non-virtual function,binded at compile time
    bptr->show();
    return 0;
}

Output
print derived class
show base class

Explanation: In the above code, the print() function is declared with the virtual keyword so it will be bound at runtime and show() is non-virtual so it will be bound during compile time.

Note: If we have created a virtual function in the base class and it is being overridden in the derived class then we don’t need a virtual keyword in the derived class, functions are automatically considered virtual functions in the derived class.

Real-Life Example to Understand the Implementation of Virtual Function

Consider employee management software for an organization.

  • We make a base class Employee with some common functions like raiseSalary() and promote(). These are declared virtual, so they can be overridden.
  • Different types of employees (Manager, Engineer) inherit from Employee and override these functions with their own specific logic.
  • In main(), we keep a list of Employee* pointers (pointing to both Manager and Engineer objects).
  • When we loop through the list and call functions like raiseSalary() or promote(), polymorphism ensures that the correct version (Manager’s or Engineer’s) is called depending on the actual object type — even though we are using base class pointers.
C++
#include <iostream>
using namespace std;

class Employee
{
  public:
    virtual void raiseSalary()
    {
        cout << "Employee salary raised (general)" << endl;
    }

    virtual void promote()
    {
        cout << "Employee promoted (general)" << endl;
    }

    // virtual destructor
    virtual ~Employee()
    {
    }
};

// Derived class: Manager
class Manager : public Employee
{
  public:
    void raiseSalary() override
    {
        cout << "Manager salary raised with incentives" << endl;
    }

    void promote() override
    {
        cout << "Manager promoted to Senior Manager" << endl;
    }
};

// Derived class: Engineer
class Engineer : public Employee
{
  public:
    void raiseSalary() override
    {
        cout << "Engineer salary raised with bonus" << endl;
    }

    void promote() override
    {
        cout << "Engineer promoted to Senior Engineer" << endl;
    }
};

int main()
{

    // Create different employees
    Manager m;
    Engineer e;

    // Array of base class pointers
    Employee *employees[2] = {&m, &e};

    // Raise salary for all employees
    cout << "--- Raising Salaries ---" << endl;
    for (int i = 0; i < 2; i++)
    {
        employees[i]->raiseSalary();
    }

    // Promote all employees
    cout << "\n--- Promotions ---" << endl;
    for (int i = 0; i < 2; i++)
    {
        employees[i]->promote();
    }

    return 0;
}

Output
--- Raising Salaries ---
Manager salary raised with incentives
Engineer salary raised with bonus

--- Promotions ---
Manager promoted to Senior Manager
Engineer promoted to Senior Engineer

How does the compiler perform runtime resolution?

The compiler maintains two things to serve this purpose:

  • vtable : A table of function pointers, maintained per class.
  • vptr : A pointer to vtable, maintained per object instance (see this for an example).
actual_object

The compiler adds additional code at two places to maintain and use vptr.

  • Code in every constructor. This code sets the vptr of the object being created. This code sets vptr to point to the vtable of the class. 
  • Code with polymorphic function call (e.g. bp->show() in above code). Wherever a polymorphic call is made, the compiler inserts code to first look for vptr using a base class pointer or reference (In the above example, since the pointed or referred object is of a derived type, vptr of a derived class is accessed). Once vptr is fetched, vtable of derived class can be accessed. Using vtable, the address of the derived class function show() is accessed and called.

Rules for Virtual Functions

The rules for the virtual functions in C++ are as follows:

  • Virtual functions are defined in the base class and can be overridden in derived classes (not mandatory; base version is used if not overridden).
  • They must have the same prototype in base and derived classes.
  • They are used through a base class pointer or reference to achieve runtime polymorphism.
  • A class may have a virtual destructor in case of dynamic memory allocation, but never a virtual constructor.
  • Virtual functions cannot be static, but they can be friend functions of another class.

Limitations of Virtual Functions

  • Slower: The function call takes slightly longer due to the virtual mechanism and makes it more difficult for the compiler to optimize because it does not know exactly which function is going to be called at compile time.
  • Difficult to Debug: In a complex system, virtual functions can make it a little more difficult to figure out where a function is being called from.

Virtual Functions | Video
Visit Course explore course icon

Explore