I can't use subclass specific funcitons when I add them to a vector using parent class pointers

I am trying to create an "alien invaders" game by myself. In order to create enemies and player, I created a class called "entity" and made subclasses of it. Like Player, shootingEnemy, IdleEnemy. When coding I realised gathering them in a vector<Entity> would make my collision detection function much easier.

After searching on the internet I learned this is called "object slicing" and makes copies of of ony le base part of objects.

So the final version became this.

int main()
{
    int BoardWidth = 50;
    int BoardLength = 30;
    vector<Bullet> bullets;
    vector<Entity*> SpaceShips;
    
    setup(SpaceShips, BoardWidth, BoardLength); 

    double ElapsedTime = 0;
    int PreviousRoundSec = 0;
    int PreviousRoundQSec = 0;

    DrawGame(BoardWidth, BoardLength, SpaceShips, bullets);
    int IsGameOver = 0;
    auto start = chrono::steady_clock::now();
    while(!IsGameOver)
    {
        // Updates EverySecond
        if ((int)(ElapsedTime / 1000) > PreviousRoundSec)
        {
            PreviousRoundSec = (int)(ElapsedTime / 1000);
            

        }

        // Updates every quarter of a second
        if ((int)(ElapsedTime / 250) > PreviousRoundQSec)
        {
            PreviousRoundQSec = (int)(ElapsedTime / 250);
            
        }

        // To keep time
        auto end = chrono::steady_clock::now();
        ElapsedTime = chrono::duration_cast<chrono::milliseconds>(end - start).count();
    }
    if (IsGameOver == 1)
    {
        // conjualations
    }
    else if (IsGameOver == 2)
    {
        // GameOver
    }

    return 0;
}

But when I try use some subclass specific functions I get an compiler error saying 'CLASS "Entity" does not have any member called "shoot"'.

I am trying to practice classes and polymorphism so I do not even know this has a solution because compiler doesn't have any way of knowing which element of this vector belongs to which subclass.

Also this is my classes header page in case needed.

class Entity
{
public:

    int x;
    int y;

    int width;
    int length;

    int hp;
    bool shooting;

public:

    Entity(int x, int y, int width, int length, int hp, bool shooting): x(x), y(y), width(width), length(length), hp(hp), shooting(shooting) {}
};

class Bullet : public Entity
{
private:
    char dir;
    int ID;

public:
    Bullet(int x, int y, char GivenDir, int GivenID) : Entity(x, y, 1, 1, 1, false) { dir = GivenDir; ID = GivenID; }

    void Move();
    void IfHit(vector<Entity>& SpaceShips);
    void IfOut();

};

class Player : public Entity
{
private:
    char action = 'a';

public:
    Player(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void GetAction();
    void Move();
    void Shoot(vector<Bullet>& bullets);
    bool IfHit(vector<Entity>& SpaceShips, vector<Bullet>& bullets);

    
};

class IdleEnemy : public Entity
{
public:
    
    IdleEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, false){}
    
    bool IfHit(Player* player, vector<Bullet> &bullets);
    void Move(char HordDir);
    
};

class ShootingEnemy : public Entity
{


public:
    ShootingEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void Shoot(vector<Bullet> &bullets);
    bool IfHit(Player* player, vector<Bullet> &bullets);
    void Move(char HordDir);
};

Upvotes: 1

Views: 82

Answers (2)

Murat Hepeyiler
Murat Hepeyiler

Reputation: 456

You need to check runtime polymorphism in C++. Let's check it out how can you do that. First of all, you need to change your Entity class interface. You need to add virtual or pure virtual functions. I have added pure virtual function;

class Entity
{
public:

    int x;
    int y;

    int width;
    int length;

    int hp;
    bool shooting;

public:

    Entity(int x, int y, int width, int length, int hp, bool shooting) : x(x), y(y), width(width), length(length), hp(hp), shooting(shooting) {}

    void virtual Move() = 0; // pure virtual function
    void virtual IfHit() = 0; // pure virtual function 
};

Virtual functions are overridable functions. Also, they have implementations but when we are talking about pure virtual functions they only provide an interface for the class. You need to override that function in your derived class. When you are implementing your derived class you need to do like this,

class Bullet : public Entity
{
private:
    char dir;
    int ID;

public:
    Bullet(int x, int y, char GivenDir, int GivenID) : Entity(x, y, 1, 1, 1, false) { dir = GivenDir; ID = GivenID; }

    void Move()override;
    void IfHit();
    void IfOut();

};

class Player : public Entity
{
private:
    char action = 'a';

public:
    Player(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void GetAction();
    void Move();
    void Shoot(vector<Bullet>& bullets);
    void IfHit()override {//code};


};

class IdleEnemy : public Entity
{
public:

    IdleEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, false) {}

    void IfHit()override;
    void Move()override;

};

class ShootingEnemy : public Entity
{


public:
    ShootingEnemy(int x, int y, int hp) : Entity(x, y, 3, 2, hp, true) {}

    void Shoot(vector<Bullet>& bullets);
    void IfHit()override;
    void Move()override;
};

These functions can be implemented either inline or in a source file. Also, there is an important point of these functions is the return value, function signature, and names' must be identical unless you do not use covariant return type.

As seen in the derived classes some of the functions are not common. I know your question how can I use that :) As mentioned ttemple in the comments you need to use dynamic_cast operator.

int main()
{

    Entity* ptr = new ShootingEnemy{ 1,2,4 };
    ptr->Move();
    ptr->IfHit();

    if (auto SE = dynamic_cast<ShootingEnemy*>(ptr))
        SE->Shoot(...);
}

dynamic_cast operator is a runtime conversion operator. It converts the type of base class pointer to the derived class. It is called downcasting. Also, it checks that base class pointer points to the target derived class. If the dynamic_cast operation is completed with fail then it returns null and if statement becomes fail. Via that way, you can use runtime polymorphism and class member functions.

By the way, avoid object slicing as possible. You are losing derived class properties.

To better understanding please refer classes dynamic_cast

Upvotes: 1

Marek Piotrowski
Marek Piotrowski

Reputation: 3076

The compiler tells you the truth. You have a pointer to an Entity, which obviously does not have Shoot method in its interface, so how could you possibly call it without any cast?

The idea behind dynamic polymorphism which you are trying to implement here is about having a common interface (your base class, Entity), with specific implementation in each sub-class. So, publicly available methods signatures are going to be common for all subclasses, but not the implementations.

From the design perspective, cleanest approach would be to rename Entity to ShootableEntity and declare a pure virtual Shoot method in there. Then all sub-classes shall provide some implementation.

If not all of them implement Shoot, yet you are trying to use them generically in such manner, maybe you should reconsider the approach, eg. create two containers - for shootable entities and for non-shootable entities. Then, when iterating over shootable-entities (instances of classes which actually subclass ShootableEntity, which contain Shoot declaration), you could call Shoot on base class' pointer without any problems.

Your Entity does not represent any common interface, however. So, if you are trying to make use of polymorphism (so, you have a pointer to the base class, yet behind that pointer there's some concrete instance), such class won't do you any good.

In fact, the doc itself has a great explanation: http://www.cplusplus.com/doc/tutorial/polymorphism/

Upvotes: 0

Related Questions