Polymorphism

Overview

├── Fighter
│     ├── Ninja
│     ├── Zombie
│     ├── Viking 
#include <iostream>
using std::cout, std::endl, std::string;

class Fighter{
protected:
    string name;
    int hp;
    int power;
protected:
    void applyDamageTo(Fighter& target, int damage) const {}
public:
    Fighter(const string &name, int hp, int power) : name(name), hp(hp), power(power) {}
    const string& getName() const {}
    bool isAlive() const {}
    void attack(Fighter& other) const {}
};


class Ninja: public Fighter{
public:
    Ninja(const string &name, int hp=100, int power=1) : Fighter(name, hp, power) {}
    void specialMove(Fighter& other) {  cout << "Ninja special move\n"; }
};


class Zombie: public Fighter{
public:
    Zombie(const string &name, int hp=100, int power=1) : Fighter(name, hp, power) {}
    void specialMove(Fighter& other) const {  cout << "Zombie special move\n"; }
};


void Engage(Fighter& f1, Fighter& f2){
    Fighter* p1 = &f1;
    Fighter* p2 = &f2;

    p1->attack(*p2);
    p2->attack(*p1);

    p1->specialMove(*p2);
    p2->specialMove(*p1);
}

The following won't compile because Fighter doesn't have specialMove so in Engage we can't call

Solution

Lets add Fighter::specialMove

What do we expect in our output?

We would expect to see

but instead we get

The reason is because Fighter* p1,p2 are unaware of the specialMove overloaded function in Ninja and Zombie

Solution

Lets add virtual before Fighter::specialMove

And now we get

this allows use to call at run-time the overloaded function or in otherwords polymorphism

but wait????

Why are we getting Fighter special move shouldn't we be Ninja special move?

Maybe we should add override to the end of our function to see what happened

And we get this compile-time error

So I guess we forgot the const on Ninja. So I guess thats why they added override, one dumb mistake ruined our code.

Lets fix it now

And here we get:

Code

So we fixed our code by adding polymorphism using virtual and making sure we overrided the functions using override. It seems that Fighter::specialMove won't be used at all, so maybe we should make it abstract?

Pure Virtual Function

We can make Fighter::specialMove pure virtual function (or abstract) meaning that we must implement it in order to use our class by adding =0 :

From now on we can't create

Because now that Fighter contains a pure virtual function it is itself an abstract class but we can still create a pointer

So now we have the following:

Lets add some game features 😃

And now we have a working game 😃

Adding group matches (not on test)

  • Here we are going to add 2 teams of 3 and let them fight each other

  • Each fighter will switch randomly and fight another oponent each turn

Here all of team1 (Kotaro,bobzie and Loki) were defeated.

Notice the power that polymorphism gives us. We are fighting using 6 different objects of 3 different classes and using the same functions to do so. The Engage function will allow us to fight regardless of which type of sub-Fighter your are.

We could scale this up to 50 versus 50 matches easily

Wait!!!!

We forgot to deallocate (delete) the memory!!!! Well we can add the following to our main

But we get

So it seems we are calling the Fighters destructor but not each sub-Fighters. This is not what we wanted.

Lets take a closer look here

Solution

Lets add virtual to Fighter

Notice that we also are calling the fighters destructor.

Rember:

Inheritance: order of construction / destruction

Class objects are constructed from the bottom up:

  1. The base.

  2. The members (may want to use init list)

  3. The derived class itself

They are destroyed in the opposite order:

  1. The derived class itself.

  2. The members.

  3. The base.

Creditsarrow-up-right

TODO add photo uml of inheritance w/o inheritance

Inheritance and Composition

A car is not an engine; it has one. And a coffee machine has a grinder and a brewing unit, but it is none of them. The car and the coffee machine integrate an engine, grinder and brewing unit via their external APIs to compose a higher level of abstraction and provide more significant value to their users.

You can do the same in software development when you design a class to keep a reference to an object and to use it in one or more of its methods.

The main reason to use composition is that it allows you to reuse code without modeling an is-a association as you do by using inheritance. It enables you to reuse code by modeling a has-a association between objects.

What is Compositionarrow-up-right

What we want?

We want to use a weapon or subclass of weapon as a black box without know the inter workings. We can also change weapons easily.

Lets start

Remember: add virtual to calculateDamage

Now lets add weapon to our Fighter class

Changes:

  • Added Attributes attr

  • in applyDamageTo everything was changed to target.attr.hp accordingly

  • Added pWeapon arg to constructor

  • attack function has changed to applyDamageTo(other, pWeapon->calculateDamage(attr));

  • add giveWeapon function

  • add delete pWeapon to destructor

Add the weapons sub-classes

Change our fighters subclasses by adding weapons in the param of our constructor

our main also looks simialar expect for the fact that we give each Figher a weapon

Final Code

Last updated