ZoOl007
ZoOl007

Reputation: 417

store pointers to different types of classes in an std::unordered_map

I have devices I need to do operations on. I have one base class BASE and a child class TYPE1. Each device is an instance of TYPE1. These get instantiated by another class depending on what is present in an xml configuration file.

class BASE {
public:
    BASE();
    virtual ~BASE();
};

class TYPE1 : public BASE {
public:
    TYPE1();
};

Now I store pointers to these instances in an std::unordered_map defined as:

std::unordered_map <std::string, TYPE1 *> myDevices;

with std::string being an identification key that is also used in configuration files.

The std::unordered_map gives me quick direct access to an object if I need it and the convenience to do the same operation on all the devices if I iterate through it using

for ( auto& elem : myDevices ) {
    //do stuff
}

The order of the devices is unimportant, hence the std::unordered_map. I use that extensively throughout the code.

Now I have the need for a TYPE2 that is also a child of BASE but nevertheless a different type.

TYPE1 and TYPE2 both have the same methods implemented - they function differently - but yield the same results

My question :

How do I modify

std::unordered_map <std::string, TYPE1 *> myDevices; 

so it accepts all types of classes like

std::unordered_map <std::string, acceptAnyTypeOfClass *> myDevices;

I'm blocked with this and while I think I could get around it it would get ugly very quickly and I would really like to do it in a clean way. Is what I'm asking possible and if so how please?

Upvotes: 1

Views: 1981

Answers (1)

Christophe
Christophe

Reputation: 73376

The first option that comes to mind would be to use a pointer to the base class, because TYPE1 and TYPE2 objects are all BASE objects:

std::unordered_map <std::string, BASE*> myDevices;

The question is then ho wto make the difference between TYPE1* pointers and TYPE2* pointers ?

As TYPE1 and TYPE2 both have the same methods implemented, the best approach would probably be to use polymorphism, using virtual functions:

class BASE {
public:
    BASE();
    virtual void doSomething()=0;  
    virtual ~BASE();
};

class TYPE1 : public BASE {
public:
    TYPE1();
    void doSometing() override { /* the code for TYPE 1 devices*/ }
};

class TYPE2 : public BASE {
public:
    TYPE2();
    void doSometing() override { /* the code for TYPE 2 devices*/ }
};

You can then invoke the functions without worrying about the true type of the base object:

std::unordered_map <std::string, BASE*> myDevices;
...
for (auto& elem : myDevices ) {
    elem->doSomething();  
}

P.S: If you create the device objects dynamically, you could consider using unique or shared pointers in the map, to avoid memory leaks.

Upvotes: 2

Related Questions