brettwhiteman
brettwhiteman

Reputation: 4452

Using polymorphism to create 'array of types'

I need to be able to retrieve a type based on an ID.

To achieve this, I am intending to create a base class for the types, which declares all the functions as pure virtual functions.

class Base
{
public:
    virtual int function1() = 0;
    virtual int function2() = 0;
};

I then create derived classes, instantiate these and store the pointers to the instances in an std::unordered_map of base pointers.

std::unordered_map<int, Base*> types;

Each element of the std::unordered_map will point to a different derived class type (i.e. no duplicates).

Then I can retrieve the type I need by getting the associated member from the std::unordered_map.

Base* ptr = types[4];

// Use type...
int num = ptr->function1();

Now I know this could come down to opinion, but is this a horrible use of C++? Is there a cleaner way to do this?

Update

I could use instance of the derived classes instead of their respective IDs, however I would likely have to dynamically allocate each one on the heap (i.e. using new), and I am going to be creating a lot of these, so the overhead from dynamic allocation could have an unwanted effect on performance.

Upvotes: 1

Views: 256

Answers (2)

Jon
Jon

Reputation: 437404

Now I know this could come down to opinion, but is this a horrible use of C++? Is there a cleaner way to do this?

I can see only one thing in the example code that could conceivably be called horrible, and that's the lack of abstraction. However, the concept itself is perfectly OK.

There are two pieces of machinery here: a polymorphic base class, and a map that gives you an object instance based on some kind of opaque id -- an int in the example, but it could also be e.g. an std::string and nothing would really change.

The base class is as classic as it gets, with the provision that it should also have a virtual destructor as per standard good practice. Apart from that there's nothing to see there.

The map is conceptually what you could call a "singleton factory". It's a factory because you call a method passing an id and you get back an object that corresponds to that id, with the only distinction that you get the same instance back every time you call the method with the same id. From a high-level perspective, this is not at all interesting to the client. The client just cares that e.g. every time they pass in 4, they get back a Widget*. How the factory gets hold of that instance in order to return the pointer is simply not important when you are looking at client code. So there's nothing out of the ordinary here also, at least on the design level.

Extending this line of thought leads to the possible deficiencies: there is no need for the client to know how the factory satisfies requests, but exposing the implementation (direct access to std::unordered_map) lets the client know everything, and it also lets them do things with the factory that they should perhaps not be allowed to do (e.g. unregistering types). This can be easily fixed by creating a new class that encapsulates the map and exposes a public interface that you have free reign to design as you see fit.

Upvotes: 2

mariusm
mariusm

Reputation: 1668

is this horrible use of C++? -- Yes, perhaps better expression is "no use of polymorphism", i.e. you are not using polymorphism (properly).

You don't need to track types in the first place as compiler does it for you through polymorphism:

1) just declare the method virtual in the base class (you already did that).

2) overwrite the method in child-classes.

like:

class Base {
public:
   virtual int myId(){ return 0; }
};
class Child1: public Base {
public:
   int myId() { return 1; }
};

Upvotes: -1

Related Questions