Before we introduce the next topic, lets take some time to explain the goal.
There's always many ways to solve a programming problem. But, which approach should we take?
Typically, a goal that we'll often here when it comes to software design is that we want our systems to be flexible, but not complex.
A common scenario where this comes up is the need to support a large range of object types, without getting bogged down.
In the context of a game, for example, that might include:
For example, when we build something like a combat system, we want to design it in such a way that each monster type has its own way of fighting.
We don't want to define all that within the combat system. We might have hundreds or thousands of monster types - imagine how complex our combat system would get.
Instead, we want to define those behaviours within the class code for each individual monster subtype. For example, the code that manages Dragons should be in the Dragon
class.
Polymorphism is the key to combining a huge range of different objects, but to keep our systems simple.
Our system just needs to care about the base class of those subtypes. The base type might be Character
, for example. Lets see a simplistic representation of our combat system:
void Combat(Character* A, Character* B) {
A->Act();
B->Act();
}
We want function calls like A->Act()
to have different effects, depending on which subtype of character A
is pointing at. For example, if A
is a Goblin
, the effect of this function call will be different than if A
were a Dragon
.
But, even though the behaviour of the Combat
function is changing, its code doesn't. That is polymorphism.
To get it working in C++, we use a combination of four things. We've already seen the first three:
Lets see an example that combines them:
#include <iostream>
using namespace std;
class Character {
public:
void Act() {
cout << "Character Attacking!" << endl;
}
};
class Goblin : public Character {
public:
void Act() {
cout << "Goblin Attacking!" << endl;
}
};
void Combat(Character* A, Character* B) {
A->Act();
B->Act();
}
int main() {
Character Character;
Goblin Goblin;
Combat(&Character, &Goblin);
}
Remember, we want the Combat
function to support any subtype of Character
, without requiring changes.
Here, we're passing it a Character
and a Goblin
. Whilst our code compiles and runs, sadly it isn't doing what we want just yet.
Given that B
is specifically a Goblin
, we want our Combat function to be calling the Goblin
version of Act
. Sadly, it's calling the Character
version twice:
Character Attacking!
Character Attacking!
That's because we're missing the 4th key part of polymorphism - virtual functions. We'll introduce those in the next lesson!
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way