Virtual functions

Comprehensive study notes, diagrams, and exam preparation for Virtual functions.

Virtual Functions

Definition

A virtual function is a member function in a base class declared with the keyword virtual that can be overridden in a derived class, and whose actual function to be executed is determined at run time based on the type of the object being referred to, not the type of the pointer or reference.

Example:

class Base {
public:
    virtual void show() {
        cout << "Base class show()" << endl;
    }
};

If a derived class overrides show(), then calling it through a base class pointer will invoke the derived class version when the object is actually of derived type.


Main Content

1. Runtime Polymorphism

  • Virtual functions make polymorphism possible at run time, meaning the decision of which function to call is postponed until the program is executing.
  • This is useful when a single interface must work with many different object types.

Example:

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

class Dog : public Animal {
public:
    void sound() override {
        cout << "Dog barks" << endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {
        cout << "Cat meows" << endl;
    }
};

If we use:

Animal* a;
Dog d;
Cat c;

a = &d;
a->sound();   // Dog barks

a = &c;
a->sound();   // Cat meows

This demonstrates dynamic binding, where the function call is resolved according to the actual object type.


2. Function Overriding and Base Class Pointers

  • A derived class can provide its own version of a virtual function; this is called overriding.
  • Virtual functions are most effective when accessed through a base class pointer or base class reference.

Important points:

  • The base class function must be declared virtual.
  • The derived class function should have the same signature to properly override it.
  • The override keyword is recommended because it helps the compiler detect mistakes.

Example:

class Shape {
public:
    virtual void area() {
        cout << "Shape area" << endl;
    }
};

class Rectangle : public Shape {
public:
    void area() override {
        cout << "Area of rectangle" << endl;
    }
};

If:

Shape* s = new Rectangle();
s->area();

The output will be:

Area of rectangle

This happens even though the pointer type is Shape*, because the object pointed to is actually a Rectangle.


3. Abstract Classes and Pure Virtual Functions

  • Virtual functions are also used to create abstract classes, which define a common interface without providing complete implementation.
  • A pure virtual function is a virtual function declared with = 0 and has no implementation in the base class.

Example:

class Employee {
public:
    virtual void calculateSalary() = 0;
};

This makes Employee an abstract class, and it cannot be instantiated directly.

Key points:

  • Pure virtual functions force derived classes to implement the function.
  • Abstract classes are useful when a base class represents a general concept but not a complete object.
  • They improve design by enforcing consistency across related classes.

Example:

class Manager : public Employee {
public:
    void calculateSalary() override {
        cout << "Manager salary calculated" << endl;
    }
};

Working / Process

  1. A base class declares a function as virtual.
  2. A derived class overrides that function with the same name and signature.
  3. When a function is called through a base class pointer or reference, C++ checks the actual object type at run time and invokes the correct overridden version.

Example:

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

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

int main() {
    Base* ptr = new Derived();
    ptr->display();   // Calls Derived display
}

How it works internally:

  • The compiler typically uses a mechanism called vtable and vptr.
  • A vtable is a table of virtual function addresses.
  • A vptr is a hidden pointer in the object that points to the vtable.
  • This enables dynamic function dispatch at run time.

Important note:

  • Virtual function calls are slightly slower than normal function calls because of this run-time lookup, but the overhead is usually very small compared to the flexibility gained.

Advantages / Applications

  • Enables runtime polymorphism, allowing a common base interface to work with many derived types.
  • Improves code reusability and extensibility because new derived classes can be added without changing existing code.
  • Widely used in real-world applications such as GUI frameworks, game development, device drivers, file handling systems, and simulation software.

Examples of applications:

  • A Shape base class with Circle, Rectangle, and Triangle derived classes.
  • An Employee base class with Manager, Developer, and Intern.
  • A Vehicle base class with Car, Bike, and Truck.

Additional benefits:

  • Promotes cleaner and more maintainable design.
  • Supports loose coupling between code components.
  • Makes it easier to implement interface-based programming.

Summary

  • Virtual functions allow a base class to define methods that derived classes can override for runtime polymorphism.
  • They are called through base class pointers or references and resolved based on the actual object type.
  • Pure virtual functions help create abstract classes and enforce implementation in derived classes.
  • Important terms to remember: virtual function, overriding, runtime polymorphism, dynamic binding, abstract class, pure virtual function, vtable, vptr