Search code examples
c++subclass

How to make an array of a parent class, made out of different subclasses


I have a class Item, and there are two classes derived from it Sword and Shield.

I want my player to have an array of items:

class Item
{
    int x;
};

class Sword : public Item
{
     int sharpness = 10;
};

class Shield : public Item
{
    int blockChance = 2;
};

class player 
{
    Item* items[4];
    player()
    {
        items[0] = new Sword;
        items[1] = new Sword;
        items[2] = new Shield;
        items[3] = new Shield;
    }
};

Is there a way I can access values of Sword or Shield from items, for example:

items[2]->blockChance;

without having a separate array of Swords and Shields

If this is not possible, then I would just have a separate array of each, but a single array containing both would make this much easier


Solution

  • In order to access the properties of a derived class via the base class, you can add a virtual method. This is a method a derived class can override to provide a different implementation. If the base class has no meaningful implementation for it, it can also be abstract (AKA pure-virtual), which will force the derived class to implement it.

    There are several other issues you should be aware of:

    1. When dealing with inheritance and virtuals method, it is important to have a virtual destructor in the base class. Otherwise the destructor of the derived classed will not be invoked when they are access via a pointer/reference (even if in your toy example this is not relevant, it's a good practice).

    2. Instead of using raw C arrays, you should favor std::array for static sized arrays, and std::vector for dynamic sized ones.

    3. Instead of using raw pointers with manual new/delete, you should use smart pointers: std::unique_ptr (the default), or std::shared_ptr (if shared ownership is required).

    4. You can use a member initialization list to init items in the constructor of class Player.

    The complete solution is demonstrated below:

    #include <array>    // for std::array
    #include <memory>   // for std::unique_ptr
    #include <iostream> // for std::cout etc.
    
    class Item
    {
    public:
        virtual ~Item() {}              // important to add a virtual destructor
        virtual int GetProperty() = 0;  // a virtual method for accessing the property
    };
    
    class Sword : public Item
    {
        int sharpness = 10;
    public:
        int GetProperty() override { return sharpness; }   // override of the virtual method
    };
    
    class Shield : public Item
    {
        int blockChance = 2;
    public:
        int GetProperty() override { return blockChance; } // override of the virtual method
    };
    
    class Player
    {
        std::array<std::unique_ptr<Item>, 4> items; // use std::array instead of a raw C array (or std::vector for a dynamic array), 
                                                    // and std::unique_ptr instead of a raw poniter.
    public:
        Player() 
            : items{ std::make_unique<Sword>(), 
                     std::make_unique<Sword>(), 
                     std::make_unique<Shield>(), 
                     std::make_unique<Shield>() }   // use a member initialization list
              {}
        std::array<std::unique_ptr<Item>, 4>& GetItems() { return items; }
    };
    
    int main()
    {
        Player player;
        std::cout << player.GetItems()[0]->GetProperty() << '\n';   // access the Sword property
        std::cout << player.GetItems()[2]->GetProperty() << '\n';   // access the Shield property
    }
    

    Output:

    10
    2
    

    Live demo - Godbolt