2

I just want to start by saying I'm still decently new to coding, and I've been trying to do research but just got stuck, as I can't find exactly what I am looking for.

I am trying to make my first game, a simple text adventure game. I was trying to make a player's inventory so I started using a vector of custom classes. After some research I found that I needed to make a base class of multiple classes.

Just for reference, I am using

vector<item*> inventory;

class Map : public item
{
public:
    void print();
    virtual string getName(){return name;};
private:
    string name = "Map";
}; 


class Compass : public item
{
public:
    virtual string getName(){return name;};
private:
    string name = "Compass";
};


class item
{
public:
    virtual string getName(){return name;};
private:
    string name = "item";
};

this is in my header file, and I have cpp files that expand on it.

When storing it I found out that I have to add virtual functions to get the name, so I added a name data member and made a getName() function and made it virtual. Eventually I was able to pull the items out and get the name, but then I found a new problem I couldn't find the answer to.

EDIT:

after using inventory.push_back(&map), trying to call (inventory[0])->print() gives me the error of "no member of print in item"

end edit

My map class has a print() function, and i need to use it from the vector. Would I have to create a virtual print() function in my item class just so it can transfer down using the virtual part, or is there an easier way of doing this?

sorry if this is simple or a stupid fix, when I was doing research all I found was people trying to find names and things, but i couldn't find how to access a method without having it in the base class.

Aman
  • 696
  • 1
  • 8
  • 26
Fyrebend
  • 123
  • 2
  • 2
  • 9
  • 1
    Please give a [mcve]. Your last few paragraphs should also show the code that you are describing in words. – Code-Apprentice Feb 12 '18 at 19:33
  • It depends what you want `print` to do. If you just want the name then you can just use the `getName` function you already have. – NathanOliver Feb 12 '18 at 19:33
  • 3
    Use `std::vector> v;` while following the polymorphic paradigm. – Ron Feb 12 '18 at 19:33
  • Classical solution is to use pointers and polymorphism. The new-age solution is `std::variant`. – Sam Varshavchik Feb 12 '18 at 19:35
  • You can cast the `item` pointer to a `map` pointer. – 001 Feb 12 '18 at 19:35
  • You would need to add a `virtual print()` and a default implementations for items that are not printable. Putting differently typed objects into the same container has significant drawbacks. Consider finding another way. – nwp Feb 12 '18 at 19:35
  • 3
    "my map class has a print() function, and i need to use it from the vector." If print does not make sense for all `item`s, you have no business calling it from a `std::vector`. Rethink your design. – Rotem Feb 12 '18 at 19:36
  • @JohnnyMopp would that let me use my map methods separately? – Fyrebend Feb 12 '18 at 19:44
  • @NathanOliver my print in map would use `cout` to print a map of the area – Fyrebend Feb 12 '18 at 19:49
  • 4
    You fell into the beginner trap of wanting to store different types in you containers which leads to (possibly even manual) dynamic memory allocations, pointer semantics, not knowing what objects you have in your container, casts and a bunch of other nasty stuff. Rethink your premise. Don't put different types in the same container and everything will become much easier. – nwp Feb 12 '18 at 19:49
  • @Fyrebend -- To add to what others have stated, if for some reason you 1) **must** store pointers to base objects in a single container, and 2) You can't change the interface to the base class (i.e., you can't put in a virtual `print` function), then prepare yourself to think of designs such as the [visitor pattern](https://stackoverflow.com/questions/10116057/visitor-pattern-explanation). – PaulMcKenzie Feb 12 '18 at 19:56
  • @PaulMcKenzie i have no problem putting a virtual print with a default of do nothing, i just didnt know if there was a more efficient way. – Fyrebend Feb 12 '18 at 20:00
  • The "item" class should be an abstract base class (interface) with a virtual destructor; however, rather than post an answer, let me refer you to https://stackoverflow.com/questions/270917/why-should-i-declare-a-virtual-destructor-for-an-abstract-class-in-c If you want, I can post an answer using your classes, but it should be pretty clear from this link. – Kenny Ostrom Feb 12 '18 at 21:30
  • [C++ Downcasting to Derived Class based off Variable](//stackoverflow.com/q/6471623) – 001 Feb 13 '18 at 13:57

1 Answers1

0

In order to use any subclass of a base class polymorphically, you would pass around a pointer or reference to the base class, and call its virtual member functions through that.

You should carefully consider alternatives, such as storing Plain-Old-Data in your vector instead of a class for everything. How many methods will actually have a different implementation between subclasses?

One data structure you could use would be a std::vector<std::unique_ptr<item> >. You would create items such as std::unique_ptr<item>(static_cast<item*>(new compass)), or as a shortcut, inventory.emplace_back(...). This handles the memory management automatically. You can then call methods such as inventory[0]->getname();. But, be sure the destructor ~item() is virtual, even if it’s just virtual ~item() = default;. Otherwise, the wrong destructor would get called!

Another alternative would be std::variant, the STL replacement for tagged unions.

Davislor
  • 14,674
  • 2
  • 34
  • 49