Search code examples
c++inheritanceoverridingvirtualsuperclass

Single container of different objects that all inherited the same class


I'm trying to accomplish something but am unsure if it's even possible.

The quick idea is, I'm writing a game and want to have a single array (or vector) of different monsters. Every class that inherits the main class Monster simply overrides its functions (but doesn't add any new ones).

Then, when I go through the list of monsters, I can just call the same functions that all of them have.

Here's some code to show what I'm trying to accomplish:

class Monster
{
    public:
        int hp;  //hit points
        int atp; //attack power
        int def; //defense

        bool attacking;
        bool defending;

        virtual void attack();
        virtual void defend();
};

void Monster::attack()
{}

void Monster::defend()
{}



class Goblin: public Monster
{
    public:
        virtual void attack() override;
        virtual void defend() override;
};

void Goblin::attack()
{
    //Goblin's attacking patterns
}

void Goblin::defend()
{
    //Goblin's defending patterns
}


class Orc: public Monster
{
    public:
        virtual void attack() override;
        virtual void defend() override;
};

void Orc::attack()
{
    //Orc's attacking patterns
}

void Orc::defend()
{
    //Orc's defending patterns
}




int main(void)
{
    //This is where I'm not sure what to do:

    //Initialize monsters.  Make some Goblins, some Orcs
    int num_monsters = 10;
    Monster* monster_list;
    monster_list = new Monster[num_monsters];

    for (int i = 0; i < num_monsters; i++)
    {
        int which = rand() % 2;
        switch (which)
        {
            case 0:  //Goblin
               monster_list[i] = new Goblin;                   
               break;
            case 1:  //Orc
               monster_list[i] = new Orc;                   
               break;
        }
    }

    bool quit = false;
    while (quit == false)
    {
        for (int i = 0; i < num_monsters; i++)
        {
            if (monster_list[i].attacking == true)
                monster_list[i].attack();
            if (monster_list[i].defending == true)
                monster_list[i].defend();                
        }
    }
}

Hopefully that illustrates what I'm trying to do. I know this doesn't work, but I'm not sure how to make it work. Thanks for any advice on this!


Solution

  • You'll need to use a vector of pointers to a base class.

    std::vector<Monster*> monsters;
    
    monsters.push_back(new FireDragon());
    monsters.push_back(new IceDragon());
    

    Then you'll be able to iterate through the monsters vector and call a common method.

    for(auto monster = monsters.begin(); monster != monsters.end(); monster++)
    {
        (*monster)->attack();
    }
    

    The classes:

    class Monster {
    public:
        virtual ~Monster() {}
        virtual void attack() = 0;
    };
    
    class FireDragon : public Monster {
    public:
        ~FireDragon();
        void attack()
        {
            std::cout << "Fire breath!" << std::endl;
        }
    };
    
    class IceDragon : public Monster {
    public:
        ~IceDragon();
        void attack()
        {
            std::cout << "Ice breath!" << std::endl;
        }
    };
    

    As a side note be sure to create virtual destructors in the derived classes or else the base class' destructor will be called.

    ETA: Here is the implementation with smart pointers:

    /*
    Use std::unique_ptr<Monster> if your implementation doesn't need to pass the
    monster objects around
    */
    
    std::vector<std::shared_ptr<Monster>> monsters;
    
    /*
    Use std::make_unique<FireDragon>() if using unique_ptr
    */
    
    monsters.push_back(std::make_shared<FireDragon>());
    monsters.push_back(std::make_shared<IceDragon>());
    
    for(auto monster : monsters)
    {
        monster->attack();
    }