2

I started writting a simple program with classes in C++ to get comfortable with OOP. I wrote a class Game that takes instances of classes Player and Unit as attributes:

#ifndef GAME_HPP
#define GAME_HPP

#include "Unit.hpp"
#include "Player.hpp"

class Game
{
public:
    //ATTRIBUTES
    Player player;
    Unit mob;
    
    //CONSTRUCTORS
    Game(Player, Unit){};
    
    //METHODS
    std::string get_n_chars(char, int);
    void print_player_character_panel();
    void print_enemy_character_panel();
    void print_character_panels();

};

#endif // GAME_HPP

I want the 'mob' attribute to take any inherited class of 'Unit', i.e.

class Goblin : public Unit
{...
int main()
{    
    Player hero{"Boi"};
    Goblin goblin;

    Game game1(hero, goblin);
    
    return 0;
}

But when I do this then 'mob' only has the attributes and methods of a 'Unit'. Like this:

https://i.stack.imgur.com/nShv3.png

And even attributes from the contructor changed:

https://i.stack.imgur.com/mPrW2.png

Can I somehow declare an instance of any inherited class of 'Unit' as mob attribute and for it to remain an instance of that class instead of 'Unit.'

wstyczen
  • 17
  • 4
  • 5
    You have fallen victim to [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). Polymorphism works only through pointers, or references. – Algirdas Preidžius Nov 12 '20 at 13:30
  • 2
    You need `std::unique_ptr`. Or a pointer to `Unit` + `new` (the latter can be good for practice, but is not recommended in real code). – HolyBlackCat Nov 12 '20 at 13:31
  • @HolyBlackCat: You don't know that OP needs any pointer at all. – einpoklum Nov 12 '20 at 13:32
  • 2
    @einpoklum OP asked for a variable that store any class derived from `Unit`. This requires a pointer (unless you accept some tradeoffs, like listing all the classes in advance for a `std::variant`). – HolyBlackCat Nov 12 '20 at 13:33
  • Does this answer your question? [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – Karl Knechtel Nov 12 '20 at 13:36

1 Answers1

1

tl;dr: Consider using a variant.

Can I somehow declare an instance of any inherited class of 'Unit' as mob attribute and for it to remain an instance of that class instead of 'Unit.'

As long as you are willing to state, at compile time, which classes inherit Unit, then - yes, sort of. You can use a std::variant - which is a fancier of form of a union.

Supposing your unit types are Goblin, Troll and Wizard, this would be: std::variant<Goblin, Troll, Wizard>.

Note, however, that a variant class is not the same as the base class that is common to all of the variant types. In other words, std::variant<Goblin, Troll, Wizard> may be a unit in our minds, but it is not a Unit as far as the C++ compiler is concerned. So you will have to do a bit of work to run code which takes Unit& or Unit*, or methods of Unit.

Here's a tutorial on using std::variant, which I recommend if you haven't worked with it before:

Simplify C++: Modern C++ Features – std::variant and std::visit

einpoklum
  • 118,144
  • 57
  • 340
  • 684