Menu

C++ Virtual Functions: Polymorphism Explained

Virtual functions let a base-class pointer call the derived class's version of a method at runtime. Learn virtual, override, abstract classes, and why a base destructor must be virtual.

This page includes runnable editors - edit, run, and see output instantly.

Why Inheritance Alone Isn't Enough

On the previous page you built a class hierarchy: a derived class inherits members from its base. But there's a catch. When you call a method through a base-class pointer, C++ decides which function to run based on the pointer's type, not the object's real type. So a Animal* that actually points to a Dog still calls Animal's version of a method.

That's almost never what you want. Most of the time you have a collection of base-class pointers, each pointing to a different derived object, and you want each one to behave as itself. Virtual functions make that happen.

The object is genuinely a Dog, yet a->speak() ran Animal::speak(). Because speak isn't virtual, the compiler picked the function at compile time from the static type Animal*. This is the bug virtual functions exist to fix.

Making a Function Virtual

Add the virtual keyword to the base-class method. Now the call is resolved at runtime based on the object's actual type - this is dynamic dispatch.

One loop over Animal*, three different behaviors. The base pointer "knows" the real type at runtime and dispatches accordingly. This single mechanism - one interface, many implementations - is what polymorphism means in C++.

Note virtual only needs to appear on the base declaration; once a function is virtual, it stays virtual in every derived class automatically. Writing it again in the derived class is optional and redundant.

Always Use the override Keyword

In the example above, each derived method is marked override. It's optional for the code to work, but you should treat it as mandatory. override (C++11) asks the compiler to verify that you really are overriding a base virtual function with a matching signature. If you get the signature subtly wrong, you get a clear error instead of a silent bug.

struct Animal {
    virtual void speak() const { }   // note: const
};

struct Dog : Animal {
    void speak() { }            // NOT const - this is a NEW function, not an override!
    void speak() override { }   // error: 'speak' does not override - tells you immediately
};

Without override, the first speak() compiles fine but never gets called through an Animal*, because its signature differs from the base (missing const). You'd spend an afternoon wondering why your override does nothing. With override, the compiler catches the mismatch on the spot. Add it to every overriding function.

Pure Virtual Functions and Abstract Classes

Sometimes the base class has no sensible default - what sound does a generic "Animal" make? In that case, declare the function pure virtual by assigning = 0. This gives it no body and turns the class into an abstract class that cannot be instantiated on its own. It only exists to define an interface that derived classes must fulfill.

Every concrete subclass must implement area(), or it stays abstract too. This is how C++ expresses "interfaces": an abstract class with only pure virtual functions is the C++ equivalent of an interface in languages like Java.

The Virtual Destructor Rule

This is the gotcha that bites everyone at least once. When you delete an object through a base-class pointer, C++ calls the destructor it finds - and if that destructor is not virtual, it only runs the base destructor. The derived part is never destroyed, leaking whatever it owned. The standard calls this undefined behavior.

The fix is one word: make the base destructor virtual. Then delete p runs ~Derived first, then ~Base, exactly as it should.

struct Base {
    virtual ~Base() { cout << "~Base\n"; }   // correct
};
// now: ~Derived then ~Base

Rule of thumb: the moment a class has any virtual function, give it a virtual destructor too. If a class is meant to be a base class used through pointers, its destructor must be virtual.

Common Mistakes and Gotchas

A few more traps to watch for once you're comfortable with virtual functions:

Object slicing. If you pass or store a derived object by value in a base variable, the derived part is "sliced off" and you're left with a plain base object - virtual dispatch no longer reaches the override. Always use pointers or references for polymorphism:

Dog d;
Animal a = d;   // SLICED: a is now just an Animal, the Dog part is gone
a.speak();      // runs Animal::speak even though virtual

Animal& ref = d;   // OK: reference keeps the real type
ref.speak();       // runs Dog::speak

Don't call virtual functions from constructors or destructors. During construction the derived part doesn't exist yet, so a virtual call resolves to the current class's version, not the derived override - rarely what you intend.

Virtual dispatch has a small cost. Each virtual call goes through a hidden table of function pointers (the "vtable"), one indirection per call. It's cheap, but not free, so don't make a function virtual unless you actually need overriding.

Calling the base version on purpose. Inside an override you can still invoke the base implementation explicitly with Base::method() - useful when the derived behavior extends rather than replaces the base.

Next: Operator Overloading

Virtual functions let your objects customize their behavior through a shared interface. The next page shows how to customize the operators that act on your objects: with operator overloading you can teach your own types to respond to +, ==, <<, and more, so a Vector + Vector or cout << myObject reads as naturally as it does for built-in types.

Frequently Asked Questions

What is a virtual function in C++?

A virtual function is a member function declared with the virtual keyword in a base class so that, when you call it through a base-class pointer or reference, C++ runs the derived class's override instead of the base version. This runtime selection is called dynamic dispatch and is the foundation of polymorphism.

What is the difference between a virtual and a pure virtual function?

A virtual function has a body and can be overridden. A pure virtual function is declared with = 0 and has no body in the base class - it forces every concrete derived class to provide an implementation. Any class with at least one pure virtual function is an abstract class and cannot be instantiated.

Why does a base class need a virtual destructor in C++?

If you delete a derived object through a base-class pointer and the base destructor is not virtual, only the base destructor runs - the derived part is never cleaned up, which leaks resources and is undefined behavior. Make the destructor of any class meant to be used polymorphically virtual.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED