Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/forums/cpp/)
-   -   Polymorphism (http://www.go4expert.com/forums/polymorphism-t26985/)

tonydav43 21Oct2011 09:22

Polymorphism
 
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 add(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 Vector3::add(const Vector3& rhs)
        {
        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();

}


xpi0t0s 22Oct2011 04:50

Re: Polymorphism
 
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();
}


tonydav43 22Oct2011 05:17

Re: Polymorphism
 
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

xpi0t0s 22Oct2011 05:41

Re: Polymorphism
 
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.

tonydav43 23Oct2011 16:19

Re: Polymorphism
 
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

tonydav43 23Oct2011 17:17

Re: Polymorphism
 
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];
        }


xpi0t0s 24Oct2011 01:44

Re: Polymorphism
 
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.

tonydav43 24Oct2011 14:38

Re: Polymorphism
 
Quote:

Originally Posted by xpi0t0s (Post 88348)
Interesting. Did you get any warnings from the compiler, such as "condition is always true"?

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

xpi0t0s 24Oct2011 17:40

Re: Polymorphism
 
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.

So, to attempt to answer your question:
(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.


All times are GMT +5.5. The time now is 04:48.