migajek
migajek

Reputation: 8614

C++, oop, list of classes (class types) and creating instances of them

I have quite a lot of classes declared, all of them are inheriting from a base (kind of abstract) class ... so all of them have common methods I'd like to use ...

now, I need to have a list of classes (not objects), later make instance of them in a loop and use the instances for calling mentioned common methods ...

the pseudo-code

class Abstract {
 void Something();
}

class TaskOne : public Abstract {
 void Something(); // method implemented somewhere below
}

class TaskTwo : public Abstract {
 void Something(); // method implemented somewhere below
}


...

list<Abstract> lst;

lst.push_back(TaskOne); // passing class type, not instance!
lst.push_back(TaskTwo); 

Abstract tmpObject = new lst[0]; //I know its wrong, just a way of expressing what I'd like to do to have instance of TaskOne!

please give any tips ...

Upvotes: 3

Views: 5953

Answers (6)

xtofl
xtofl

Reputation: 41509

You could create a templated factory object:

struct IFactory { virtual IBaseType* create() = 0; };

template< typename Type > struct Factory : public IFactory {
   virtual Type* create( ) {
      return new Type( );
   }
};

struct IBaseType { /* common methods */ virtual ~IBaseType(){} };

IFactory* factories[] = {
  new Factory<SubType1>
, new Factory<SubType2>
// ...
};

std::vector<IBaseType*> objects;
objects.push_back( factories[1]->create() ); // and another object!

// don't forget to delete the entries in the 
// vector before clearing it (leak leak)

Upvotes: 4

KitsuneYMG
KitsuneYMG

Reputation: 12901

Boost.MPL is the answer here. Don't listen to Hassan Syed.

Example:

namespace mpl=boost::mpl;
typedef mpl::vector< CLASS1, CLASS2,...,CLASSN > class_list_a;
typedef mpl::push_back< class_list_a ANOTHER_CLASS>::type class_list_b;
typedef mpl::push_back<
    typename mpl::push_back<
        class_list_b,
        ANOTHER_TYPE_1>::type, 
    ANOTHER_TYPE_2>::type
class_list;

struct functor {
    template<class U> void operator(U& u) {
        u.something();
    }
};
...
// in some function
boost::mpl::for_each<class_list>( functor() );

EDIT: BOOST_PP could also work

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define MY_CLASSES (TYPE_1)(TYPE_2)(TYPE_N)
#define MY_FUNCTION(r, data, elem) elem() . data;
#define CALL_SOMETHING_ON(_CLASSES_, _ARGS_) \
    BOOST_PP_SEQ_FOR_EACH( MY_FUNCTION, someThing( _ARGS_ ), _CLASSES_ )
int foo = 1;
CALL_SOMETHING_ON( MY_CLASSES, foo )

running cpp foo.c yields:

int foo = 1;
TYPE_1() . someThing( foo ); \
TYPE_2() . someThing( foo ); \
TYPE_N() . someThing( foo );    

Upvotes: 2

Igor
Igor

Reputation: 27250

I would go with the templated factory, like xtofl proposed, but simplify its usage

struct IFactory { virtual IBaseType* create() = 0; };

template< typename Type > struct Factory : public IFactory {
   virtual Type* create( ) {
      return new Type( );
   }
};

list<IFactory*> lst;

lst.push_back(new Factory<TaskOne>); 
lst.push_back(new Factory<TaskTwo>); 

Abstract *tmpObject = lst[0]->create();

// don't forget to delete all the factory instances!

Upvotes: 3

Jon Benedicto
Jon Benedicto

Reputation: 10582

I'd suggest using a factory-based design. Your list stores a bunch of factory instances, and you can create an Abstract-derived instance by calling the right factory method.

For example:

class Abstract
{
    virtual void Something() = 0;
};

class TaskOne : public Abstract
{
    void Something();
};

class AbstractFactory
{
public:
    Abstract* CreateInstance();
};

template <class T> class Factory : public AbstractFactory
{
public:
    Abstract* CreateInstance()
    {
        return new T();
    }
};

...

std::vector<AbstractFactory*> Factories;

Factories.push_back(new Factory<TaskOne>());

...

Abstract *Instance = Factories[ 0 ]->CreateInstance();

Upvotes: 1

Hassan Syed
Hassan Syed

Reputation: 20485

You need to have have a list of pointers of type abstract. initialize all the pointers to null and construct to the correct class later. You don't need to pre-type the pointers before hand. You can just rely on the RTTI latter.

If you do need to pre-type them before hand than just add an enum to Abstract for the types. This saves you memory if the inherited classes are large. But you have to maintain the enum of types yourself, not the most elegant way of doing things.

If you have to initialize and pre-type them, just initialize the classes using the default constructor. But this way you are using memory when not needed. RTTI will be your friend here in this case.

Upvotes: 0

Matt Gordon
Matt Gordon

Reputation: 51

Factory pattern could work (see e.g. http://en.wikipedia.org/wiki/Factory_method_pattern). Something along the lines of:

#define stringify(a) #a
static int hashstr(const char* s) {/*hash fn of choice*/}

list<int> types;

lst.push_back(hashtr(stringify(TaskOne))); // passing class type, not instance!
lst.push_back(hashtr(stringify(TaskTwo))); 

static Abstract* Instance(int classid)
{
  switch(id)
  {
  case hashstr(stringify(TaskOne)):
    return new TaskOne;
  //etc
  }
}

You can be considerably more elegant with extra work. Embedding class id's into the class declaration as static ints is often a good start.

Upvotes: 0

Related Questions