Polymorphism means the same kind of call can produce different behavior depending on the actual object.
In C++, a virtual function lets a derived class provide its own version of a base-class function and have that version selected through a base-class reference or pointer.
Start with one simple idea:
Animal says: "Animal"
Dog says: "Woof"
Cat says: "Meow"
The smallest useful example
#include <iostream>
class Animal {
public:
virtual void speak() {
std::cout << "Animal\n";
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof\n";
}
};
int main() {
Dog dog;
Animal& pet = dog;
pet.speak();
}
Output:
Woof
The variable pet is an Animal reference, but it refers to a real Dog object. Because speak() is virtual, C++ calls Dog::speak().
Video 1: Virtual functions and override
What does virtual do?
Look at the base class:
virtual void speak()
The keyword virtual tells C++ that derived classes may provide their own version and that the program should select the appropriate overridden function when calling through a base reference or pointer.
Without getting into the internal machinery yet, think:
virtual = let the actual object choose the overridden behavior.
What does override do?
void speak() override
override tells the compiler, “I intend this function to override a virtual function from a base class.”
That is useful because the compiler can catch mistakes. If the function does not actually match a virtual base-class function, the compiler reports an error instead of silently accepting your intention.
Video 2: Runtime method selection
Add a Cat
class Cat : public Animal {
public:
void speak() override {
std::cout << "Meow\n";
}
};
Now both Dog and Cat are kinds of Animal, but each can respond differently to the same speak() call.
Dog dog;
Cat cat;
Animal& first = dog;
Animal& second = cat;
first.speak();
second.speak();
Output:
Woof
Meow
Same base-class interface. Different actual objects. Different behavior. That is the beginner version of runtime polymorphism.
How this connects to inheritance
The previous lesson, OSC++.017: Inheritance Basics, introduced base and derived classes.
Inheritance gives us the relationship:
Animal
├── Dog
└── Cat
Virtual functions add behavior to that relationship:
Animal reference → Dog object → Dog behavior
Animal reference → Cat object → Cat behavior
Simple game example
Imagine a game with different characters. Every character can perform an action, but the action is different for each character.
class Character {
public:
virtual void action() {
std::cout << "Character acts\n";
}
};
class Miner : public Character {
public:
void action() override {
std::cout << "Miner deploys an ASIC\n";
}
};
class Engineer : public Character {
public:
void action() override {
std::cout << "Engineer repairs equipment\n";
}
};
The game can work with a Character interface while each real character supplies its own action.
Video 3: Polymorphism, virtual functions, and abstract classes
Reference vs. copy
For this lesson, notice the ampersand:
Animal& pet = dog;
This makes pet a reference to the existing dog object. We are not creating a separate Animal copy.
This connects back to OSC++.006: References and Pass by Reference.
Common beginner mistakes
- Forgetting
virtualon the base-class function when runtime overriding is intended. - Leaving off
overrideand making a signature mistake that the compiler could have caught. - Thinking polymorphism means every function must be virtual.
- Confusing overriding with overloading. Overriding replaces inherited virtual behavior in a derived class; overloading uses the same function name with different parameter lists.
- Trying to learn vtables before understanding the simple behavior first.
Quick practice
- Create a base class named
Vehicle. - Add a virtual function named
move(). - Create
CarandBikeclasses that inherit fromVehicle. - Override
move()in both classes. - Create a
Carand aBike. - Use
Vehicle&references to callmove()and observe the different output.
Key takeaway
virtual enables runtime selection of overridden behavior through a base-class interface, and override lets the compiler verify that a derived function really overrides a base virtual function. In plain English: the program can say “speak” to an Animal and still get the correct Dog or Cat behavior.

Leave a comment