innocent_bystander
innocent_bystander

Reputation: 389

Best way for derived classes to carry different data types in C++

What is the most elegant way to provide an interface in C++ that accepts derived class types that carry with them different data type members that then need to be retrieved later. The example below illustrates this where the Container class provides methods to "post" an Item that will be some kind of derived variant of BaseItem. Later on I want to get the derived Item back and extract its value.

The main thing I want is for the Container interface (post and receive) to stay the same in the future while allowing different "Item" derived types to be defined and "passed" through it. Would template be better for this somehow; I'd rather not use RTTI. Maybe there is some simple, elegant answer to this, but right now I'm struggling to think of it.

class ItemBase {
  // common methods
};

class ItemInt : public ItemBase
{
  private:
    int dat;
  public:
    int get() { return dat; }  
};

class ItemDouble : public ItemBase
{
  private:
    double dat;
  public:
    double get() { return dat; }  
};

class Container {
 public:
   void post(int postHandle, ItemBase *e);      
   ItemBase* receive(int handle); // Returns the associated Item
};

int main()
{
   ItemInt *ii = new IntItem(5);
   Container c;
   c.post(1, ii);

   ItemInt *jj = c.receive(1); 
   int val = jj->get();  // want the value 5 out of the IntItem
}

Upvotes: 6

Views: 3802

Answers (4)

Dietmar Kühl
Dietmar Kühl

Reputation: 153955

A pure template approach doesn't work because you apparently want to have mixed types in your container. You could work with something like Boost's any although I think you need to restore the actual. What I think is called for in this case is a base class exposing the type-independent and virtual methods plus a templatized derived class to hold the actual items:

class Base {
public:
    virtual ~Base() {}
    virtual void post() = 0;
};

template <typename T>
class Item: public Base {
public:
    Item(T const& value): value_(value) {}
    void post() { std::cout << "posting " << this->value_ << "\n"; }
private:
    T value_;
};

This approach avoids the need to write any derived Item class for another value type. To make creation of these beast easier you probably want to create a suitable creation function as well, e.g.

template <typename T>
std::unique_ptr<Base> make_item(T const& value) {
    return std::unique_ptr<Base>(new Item<T>(value));
}

A std::unique_ptr<Base> is returned to make sure that the allocated object is released (if you don't use C++2011 you can used std::auto_ptr<T> instead). This type can easily be converted to other pointer types, e.g. to a std::shared_ptr<Base> which is a better suited to be put into a container.

Upvotes: 0

ronag
ronag

Reputation: 51263

How about?

template<typename T>
class Item
{
  private:
    T dat;
  public:
    T get() { return dat; }  
};

class Container {
 public:
   template<typename T>
   void post(int postHandle, Item<T> *e);      

   template<typename T>
   Item<T>* receive(int handle); // Returns the associated Item
};

int main()
{
   Item<int> *ii = new Item<int>(5);
   Container c;
   c.post(1, ii);

   Item<int> *jj = c.receive<int>(1); 
   int val = jj->get();  // want the value 5 out of the IntItem
}

Upvotes: 1

Rob K
Rob K

Reputation: 8926

Your Container class looks suspiciously like a std::map. It looks to me like your ItemBase class is just a different name for "Object", the universal base class, which I think is not much different from (or better than) void*. I would avoid trying to contain items of different type in a single container. If your design seems to call for doing so, I'd rethink your design.

Upvotes: 0

Harper Shelby
Harper Shelby

Reputation: 16583

This is definitely a candidate for generic programming, rather than inheritance. Remember, generics (templates) are ideal when you want identical handling for different data types. Your ItemInt and ItemDouble classes violate OO design principles (the get() method returns different data types depending on what the actual subtype is). Generic programming is built for that. The only other answer would be a tagged data type, and I personally avoid those like the plague.

Upvotes: 6

Related Questions