C++ Virtual Functions

A C++ virtual function is a member function that can be overridden in a derived class and chosen at runtime. It lets code that uses a base class reference or pointer call behavior from the object’s actual derived type.

Virtual functions are one of the main tools behind runtime polymorphism in C++. They are most useful when several derived classes share the same base class interface.

Basic Syntax

Declare a base class member function with the virtual keyword, such as virtual void print() const;. In the derived class, write a function with the same name, return type, parameter list, and const marker, then add override.

The virtual keyword belongs in the base class. The override keyword belongs in the derived class and asks the compiler to confirm that an override is really happening.

Example

This program has one base class named Notification and two derived classes. The send() function receives a Notification reference, but the virtual function call runs the correct derived version.

#include <iostream>
#include <string>

class Notification {
private:
    std::string recipient;

public:
    Notification(std::string person) {
        recipient = person;
    }

    std::string getRecipient() const {
        return recipient;
    }

    virtual void send() const {
        std::cout << "Sending a notification to " << recipient << std::endl;
    }

    virtual ~Notification() {
    }
};

class EmailNotification : public Notification {
public:
    EmailNotification(std::string person) : Notification(person) {
    }

    void send() const override {
        std::cout << "Email sent to " << getRecipient() << std::endl;
    }
};

class TextNotification : public Notification {
public:
    TextNotification(std::string person) : Notification(person) {
    }

    void send() const override {
        std::cout << "Text message sent to " << getRecipient() << std::endl;
    }
};

void deliver(const Notification& message) {
    message.send();
}

int main(void) {
    EmailNotification email("Asha");
    TextNotification text("Ben");

    deliver(email);
    deliver(text);

    return 0;
}

Output:

Email sent to Asha
Text message sent to Ben

How The Example Works

Notification declares send() as virtual. That means a call such as message.send() can be resolved using the real object type at runtime.

When deliver(email) runs, the reference parameter message has the type const Notification&, but it refers to an EmailNotification object. Because send() is virtual, C++ calls EmailNotification::send().

When deliver(text) runs, the same function calls TextNotification::send(). One function can therefore work with different derived types through the same base class interface.

Without Virtual

If send() were not virtual in the base class, then message.send() would use the base class version because message is declared as a Notification reference. The derived classes would still have functions named send(), but calls through the base reference would not dispatch to them.

Use override

The override keyword is not required, but it is strongly recommended. It catches mistakes such as misspelling the function name or forgetting a matching const.

For example, void send() is not the same as void send() const. With override, the compiler reports the mismatch instead of quietly creating a different function.

Virtual Destructors

If a class is meant to be used polymorphically, its destructor should usually be virtual. This matters when deleting a derived object through a base class pointer.

The example includes virtual ~Notification() for that reason. Even though the destructor body is empty, making it virtual ensures the correct destructor chain runs for derived objects.

Common Mistakes

  • Adding override in the derived class but forgetting virtual in the base class.
  • Changing the parameter list or const marker so the derived function does not actually override the base function.
  • Expecting runtime dispatch when calling methods on a plain base class object instead of a base reference or pointer.
  • Using a polymorphic base class without a virtual destructor.

Takeaway: virtual functions let base class references and pointers call the derived behavior that matches the real object at runtime.