# Polymorphism

Discussion in 'C++' started by tonydav43, Oct 21, 2011.

1. ### tonydav43New Member

Joined:
Aug 10, 2011
Messages:
22
0
Trophy Points:
0
I am part way through a course, and we have just finished overloading a printstats game example. It then says this is not an ideal method, and polymorphism is better. Thing is, even though it talks about polymorphism, it does not give you a working example for the console output game we created. I have read almost every article on google, and still cannot get my head around polymorphism. Could someone please amend the code below, so the stats are printed out using polymorphism. The Vector3.h, and Vector3.cpp, are old reusable codes. I need it to also print out the number of missles the fighter ship has. There is alot more classes involved, but I have reduced the code for brevity. I just think that if I can see polymorphism working on this, I might get a better understanding of it. This is not a project, this is something I am working on.
Code:
```// Vector3.h

#ifndef VECTOR3_H
#define VECTOR3_H

#include <iostream>

class Vector3
{
public:

Vector3();

Vector3(float coords[3]);

Vector3(float x, float y, float z);

bool equals(const Vector3& rhs);

Vector3 sub(const Vector3& rhs);

Vector3 mul(float scaler);

float length();

void normalize();

float dot(const Vector3& rhs);

float* toFloatArray();

void print();

void input();

float mX;
float mY;
float mZ;
};
#endif
```
Code:
```/ Vector3.cpp

#include <iostream>
#include <cmath>
#include "Vector3.h"

using namespace std;

Vector3::Vector3()
{
mX = 0.0f;
mY = 0.0f;
mZ = 0.0f;
}

Vector3::Vector3(float coords[3])
{
mX = coords[0];
mY = coords[1];
mZ = coords[2];
}

Vector3::Vector3(float x, float y, float z)
{
mX = x;
mY = y;
mZ = z;
}

bool Vector3::equals(const Vector3& rhs)
{
return
mX == rhs.mX &&
mY == rhs.mY &&
mZ == rhs.mZ;
}

{
Vector3 sum;
sum.mX = mX + rhs.mX;
sum.mY = mY + rhs.mY;
sum.mZ = mZ + rhs.mZ;
return sum;
}

Vector3 Vector3::sub(const Vector3& rhs)
{
Vector3 dif;
dif.mX = mX - rhs.mX;
dif.mY = mY - rhs.mY;
dif.mZ = mZ - rhs.mZ;
return dif;
}

Vector3 Vector3::mul(float scaler)
{
Vector3 p;
p.mX = mX * scaler;
p.mY = mY * scaler;
p.mZ = mZ * scaler;
return p;
}

float Vector3::length()
{
return sqrtf(mX*mX + mY*mY + mZ*mZ);
}

void Vector3::normalize()
{
float len = length();
mX /= len;
mY /= len;
mZ /= len;
}

float Vector3::dot(const Vector3& rhs)
{
float dotP = mX*rhs.mX + mY*rhs.mY + mZ*rhs.mZ;
return dotP;
}

float* Vector3::toFloatArray()
{
return &mX;
}

void Vector3::print()
{
cout << "<" << mX << ", " << mY << ", " << mZ << "> \n";
}

void Vector3::input()
{
cout << "Enter x: ";
cin >> mX;
cout << "Enter y: ";
cin >> mY;
cout << "Enter z: ";
cin >> mZ;
}
```
Code:
```// Spaceship.h
#ifndef SPACESHIP_H
#define SPACESHIP_H

#include <iostream>
#include <string>
#include "Vector3.h"

using namespace std;

class Spaceship
{
public:
Spaceship();
Spaceship(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

void landed();
void attacking();
void printStats();

protected:

string mName;
Vector3 mPosition;
Vector3 mVelocity;
int mFuelLevel;
int mDamage;
};

class HumanShip : public Spaceship
{
public:
HumanShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

};

class HumanFighterShip : public HumanShip
{
public:
HumanFighterShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int numMissles);

void fireLaserGun();
void fireMissle();

private:
int mNumMissles;
};

#endif
```
Code:
```// Spaceship.cpp

#include "Spaceship.h"
#include <iostream>
#include "Vector3.h"

using namespace std;

Spaceship::Spaceship()
{
mName			= "DefaultName";
mPosition		= Vector3(0.0f, 0.0f, 0.0f);
mVelocity		= Vector3(0.0f, 0.0f, 0.0f);
mFuelLevel		= 100;
mDamage			= 0;

}

Spaceship::Spaceship(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

HumanShip::HumanShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

void Spaceship::landed()
{
cout << mName <<" has landed" << endl;
}

void Spaceship::attacking()
{
cout << mName << " is attacking" << endl;
}

void Spaceship::printStats()
{
cout << "Name = " << mName << endl;
cout << "Position = <";
cout << mPosition.mX << ", ";
cout << mPosition.mY << ", ";
cout << mPosition.mZ << ">" << endl;
cout << "Velocity = <";
cout << mVelocity.mX << ", ";
cout << mVelocity.mY << ", ";
cout << mVelocity.mZ << ">" << endl;
cout << "Fuel Level = " << mFuelLevel << endl;
cout << "Damage = " << mDamage << endl;

}

HumanFighterShip::HumanFighterShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int numMissles)

:HumanShip(name,pos, vel, fuel, damage)

{
mNumMissles = numMissles;
}

void HumanFighterShip::fireLaserGun()
{
cout << "Firing " << mName << " laser guns." << endl;
}

void HumanFighterShip::fireMissle()
{
if(mNumMissles > 0)
{
cout << "Firing " << mName << " missles." << endl;

mNumMissles--;
}
else
cout << "Out of missles." << endl;
}

```
Code:
```// Main.cpp

#include <iostream>
#include "SpaceShip.h"
#include "Vector3.h"

using namespace std;

int main()
{
HumanFighterShip fighter("Human Fighter", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 10);

fighter.printStats();

cout << endl;

fighter.attacking();
fighter.fireLaserGun();
fighter.fireMissle();

}
```

2. ### xpi0t0sMentor

Joined:
Aug 6, 2004
Messages:
3,012
203
Trophy Points:
63
Occupation:
Senior Support Engineer
Location:
England
You need a few more things before polymorphism can work here.
- HumanFighterShip and HumanShip need an implementation of printStats()
- an array containing a number of HumanFighterShips and HumanShips. This array should be defined as an array of SpaceShips, e.g. SpaceShip *arr[10]; Assigning one of those two to an array entry works fine, even though the types are technically different. For example:
Code:
```arr[0]=new HumanFighterShip();
```
- possibly some HumanFighterShip- and HumanShip- specific attributes for HumanFighterShip:: printStats() and HumanShip:: printStats() to access - so that you know it's using those two functions and not SpaceShip:: printStats() (spaces added to stop : p displaying as :p )

Then polymorphism can be demonstrated easily with:
Code:
```for (int i=0; i<10; i++)
{
arr[i]->printStats();
}
```

3. ### tonydav43New Member

Joined:
Aug 10, 2011
Messages:
22
0
Trophy Points:
0
Hi Thanks for the reply. I only have 1 instance of human fighter ship at present, but I understand what you are saying about the array. I have re-wittne the code as follows, and any comments would be appreciated. The code runs fine and prints to console all the stats including the additional humanfightership missile stat:
Code:
```
// Main.cpp

#include <iostream>
#include "SpaceShip.h"
#include "Vector3.h"

using namespace std;

int main()
{

Spaceship* ship = new HumanFighterShip("Human Fighter", Vector3(5.0f, 6.0f, -3.0f), Vector3(1.0f, 2.0f, 3.0f),100, 0, 10);

ship->printStats();

cout << endl;

HumanFighterShip* fighterShip = (HumanFighterShip*) ship;

fighterShip->attacking();
fighterShip->fireLaserGun();
fighterShip->fireMissle();

delete ship;

}
```
Code:
```
// Spaceship.cpp

#include "Spaceship.h"
#include <iostream>
#include "Vector3.h"

using namespace std;

Spaceship::Spaceship()
{
mName			= "DefaultName";
mPosition		= Vector3(0.0f, 0.0f, 0.0f);
mVelocity		= Vector3(0.0f, 0.0f, 0.0f);
mFuelLevel		= 100;
mDamage			= 0;

}

Spaceship::Spaceship(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

HumanShip::HumanShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

void Spaceship::landed()
{
cout << mName <<" has landed" << endl;
}

void Spaceship::attacking()
{
cout << mName << " is attacking" << endl;
}

void Spaceship::printStats()
{
cout << "Name = " << mName << endl;
cout << "Position = <";
cout << mPosition.mX << ", ";
cout << mPosition.mY << ", ";
cout << mPosition.mZ << ">" << endl;
cout << "Velocity = <";
cout << mVelocity.mX << ", ";
cout << mVelocity.mY << ", ";
cout << mVelocity.mZ << ">" << endl;
cout << "Fuel Level = " << mFuelLevel << endl;
cout << "Damage = " << mDamage << endl;

}

void HumanFighterShip::printStats()
{
Spaceship::printStats();
cout << "Missles = " << mNumMissles << endl;
}

HumanFighterShip::HumanFighterShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int numMissles)

:HumanShip(name,pos, vel, fuel, damage)

{
mNumMissles = numMissles;
}

void HumanFighterShip::fireLaserGun()
{
cout << "Firing " << mName << " laser guns." << endl;
}

void HumanFighterShip::fireMissle()
{
if(mNumMissles > 0)
{
cout << "Firing " << mName << " missles." << endl;

mNumMissles--;
}
else
cout << "Out of missles." << endl;
}
```
Code:
```// Spaceship.h
#ifndef SPACESHIP_H
#define SPACESHIP_H

#include <iostream>
#include <string>
#include "Vector3.h"

using namespace std;

class Spaceship
{
public:
Spaceship();
Spaceship(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

void landed();
void attacking();
virtual void printStats();

protected:

string mName;
Vector3 mPosition;
Vector3 mVelocity;
int mFuelLevel;
int mDamage;
};

class HumanShip : public Spaceship
{
public:
HumanShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

};

class HumanFighterShip : public HumanShip
{
public:
HumanFighterShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int numMissles);

void fireLaserGun();
void fireMissle();

void printStats();

private:
int mNumMissles;
};

#endif
```
I am sure that I am now using polymorphism, but correct me if I am wrong

4. ### xpi0t0sMentor

Joined:
Aug 6, 2004
Messages:
3,012
203
Trophy Points:
63
Occupation:
Senior Support Engineer
Location:
England
Yes, you are now using it. It's "more satisfying" though to do what I suggested and manage a bunch of Human*Ships using a collection of pointers to SpaceShips, but it's not necessary.

5. ### tonydav43New Member

Joined:
Aug 10, 2011
Messages:
22
0
Trophy Points:
0
I have got the code to work using polymorphism, so I thought I would give an array of human fighter ships a go. The code complies, prints then says test.exe has stopped working, and I cannot figure out why. Can you see anything from the code below?
Code:
```// Spaceship.h
#ifndef SPACESHIP_H
#define SPACESHIP_H

#include <iostream>
#include <string>
#include "Vector3.h"

using namespace std;

class Spaceship
{
public:
Spaceship();
Spaceship(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

virtual void printStats();

protected:

string mName;
Vector3 mPosition;
Vector3 mVelocity;
int mFuelLevel;
int mDamage;
};

class HumanShip : public Spaceship
{
public:
HumanShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage);

};

class HumanFighterShip : public HumanShip
{
public:
HumanFighterShip(
const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int type,
int numMissles);

void printStats();

private:

int mType;
int mNumMissles;
};

#endif
```
Code:
```
// Main.cpp

#include <iostream>
#include "SpaceShip.h"
#include "Vector3.h"

using namespace std;

int main()
{

Spaceship* fighterHuman[5];

fighterHuman[0] = new HumanFighterShip("Human Fighter Class 1", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 1, 10);

fighterHuman[1] = new HumanFighterShip("Human Fighter Class 2", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 2, 10);

fighterHuman[2] = new HumanFighterShip("Human Fighter Class 3", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 3, 10);

fighterHuman[3] = new HumanFighterShip("Human Fighter Class 4", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 4, 10);

fighterHuman[4] = new HumanFighterShip("Human Fighter Class 5", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 5, 10);

for(int i = 0; i < 5; ++i)
{

fighterHuman[i]->printStats();
cout << endl;

}

for(int i = 0; 1 < 5; ++i)
{

delete fighterHuman[i];
}

}
```
Code:
```
// Spaceship.cpp

#include "Spaceship.h"
#include <iostream>
#include "Vector3.h"

using namespace std;

Spaceship::Spaceship()
{
mName			= "DefaultName";
mPosition		= Vector3(0.0f, 0.0f, 0.0f);
mVelocity		= Vector3(0.0f, 0.0f, 0.0f);
mFuelLevel		= 100;
mDamage			= 0;

}

Spaceship::Spaceship(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

HumanShip::HumanShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage)
{
mName			= name;
mPosition		= pos;
mVelocity		= vel;
mFuelLevel		= fuel;
mDamage			= damage;

}

void Spaceship::printStats()
{
cout << "Name = " << mName << endl;
cout << "Position = <";
cout << mPosition.mX << ", ";
cout << mPosition.mY << ", ";
cout << mPosition.mZ << ">" << endl;
cout << "Velocity = <";
cout << mVelocity.mX << ", ";
cout << mVelocity.mY << ", ";
cout << mVelocity.mZ << ">" << endl;
cout << "Fuel Level = " << mFuelLevel << endl;
cout << "Damage = " << mDamage << endl;

}

void HumanFighterShip::printStats()
{
Spaceship::printStats();
cout << "Fighter Class = " << mType << endl;
cout << "Missles = " << mNumMissles << endl;

}

HumanFighterShip::HumanFighterShip(const string& name,
const Vector3& pos,
const Vector3& vel,
int fuel,
int damage,
int type,
int numMissles)

:HumanShip(name,pos, vel, fuel, damage)

{
mType = type;
mNumMissles = numMissles;
}

```
I have not included the Vector3.h and cpp file

6. ### tonydav43New Member

Joined:
Aug 10, 2011
Messages:
22
0
Trophy Points:
0
Sorted the problem

Code:
```for(int i = 0; i < 5; ++i)
{

fighterHuman[i]->printStats();
cout << endl;

}

for(int i = 0; 1 < 5; ++i) // Should be i < 5
{

delete fighterHuman[i];
}
```

7. ### xpi0t0sMentor

Joined:
Aug 6, 2004
Messages:
3,012
203
Trophy Points:
63
Occupation:
Senior Support Engineer
Location:
England
Interesting. Did you get any warnings from the compiler, such as "condition is always true"?

If not, try switching warnings to the maximum level, recompile the program with the error still in it, and see what it tells you. Warnings can be a good place to start looking for the reason for unexplained crashes.

I tried to spot the error in the code but I missed it. 1 looks too much like i. There should be a lesson for you here regarding choice of variable names. Well done for spotting the error yourself though. Debugging is probably 90% of programming - certainly over half - and too many people post here going "it crashes, find it for me, waaah" without realising they're throwing away half the "fun" of programming.

8. ### tonydav43New Member

Joined:
Aug 10, 2011
Messages:
22
0
Trophy Points:
0
No did not get warning, and will change the warning level.

Have another question using arrays. I have objects in HumanFighterShip, which are not objects of Spaceship, which are fire laser and fire missiles. The code (snippet) I have at present is as follows:
Code:
```int main()
{

HumanFighterShip* fighterHuman[3];

fighterHuman[0] = new HumanFighterShip("Human Fightership Class 1", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 1, 100);

fighterHuman[1] = new HumanFighterShip("Human Fightership Class 2", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 2, 110);

fighterHuman[2] = new HumanFighterShip("Human Fightership Class 3", Vector3(5.0f, 6.0f, -3.0f),
Vector3(1.0f, 2.0f, 3.0f),100, 0, 3, 120);

for(int i = 0; i < 3; ++i)
{
fighterHuman[i]->attacking();
fighterHuman[i]->fireLaserGun();
fighterHuman[i]->fireMissle();
fighterHuman[i]->printStats();
cout << endl;
}
for(int i = 0; i < 3; ++i)
{
delete fighterHuman[i];
}
```
Code:
```//Spaceship.cpp
void HumanFighterShip::fireLaserGun()
{

cout << "Firing " << mName << " laser guns." << endl;
}

void HumanFighterShip::fireMissle()
{

if(mNumMissles > 0)
{
cout << "Firing " << mName << " missles." << endl;

mNumMissles--;
}
else
cout << "Out of missles." << endl;
}
```
Question is, if I change the following piece of code:
Code:
```
HumanFighterShip* fighterHuman[3];

to

Spaceship* fighterHuman[3];
```
it tells me that the 2 objects mentioned are not members of Spaceship. I might be barking up the wrong tree, but should you not use a pointer to the base class, or would that be wrong? If it is and if you dont mind, could you explain why

9. ### xpi0t0sMentor

Joined:
Aug 6, 2004
Messages:
3,012
203
Trophy Points:
63
Occupation:
Senior Support Engineer
Location:
England
Polymorphism works when you can do everything you want to every object through the interface defined in the base class. Once you start trying to use functionality not defined at the base level, you have to leave it behind, decide what kind of object it is then start doing class-specific stuff, which by definition is not polymorphic.

(1) it is wrong to use a pointer to the base class if you are trying to do stuff not defined in the base class;
(2) it is right to use a pointer to the base class if you are using polymorphism.

So the next question you need to address is this: can *all* SpaceShips fire lasers and missiles? (What about cargo ships?) If they can, then you can safely drop this stuff into SpaceShip and continue to use polymorphism. If they can't, but you could define a new subclass called ShipWithAttackyStuff from which all SpaceShips that can fire lasers and missiles can be derived, then you could use polymorphism at the level of ShipWithAttackyStuff (which might mean that you need to define something like isShipWithAttackyStuff() in the base class, so that you could then use pseudo-polymorphism when iterating over SpaceShips: call isShipWithAttackyStuff() then depending on the result call SpaceShip::Surrender() or cast the pointer to ShipWithAttackyStuff and polymorph from there.)

It's a lot of fun, this polymorphism. Especially when you get into multiple inheritance.