I really don't know how to approach this. I donβt understand half of the interface requirements you specified, so consider this experimental answer, which may not concern your problem at all.
I suggest you tell me what is missing in my approach, and I can fix it. I am omitting the templates now, as they do not seem to be relevant to the problem.
Thus, without further use, the simplest beginning uses a container of smart pointers:
#include <vector> #include <memory> struct Base { virtual void f(); }; typedef std::shared_ptr<Base> BasePtr; typedef std::vector<BasePtr> BaseContainer; struct DerivedA : Base { virtual void f(); // ... }; // further derived classes
Using:
int main() { BaseContainer v; v.push_back(BasePtr(new DerivedB)); v.push_back(BasePtr(new DerivedC(true, 'a', Blue))); BasePtr x(new DerivedA); some_func(x); x->foo() v.push_back(x); v.front()->foo(); }
If you have some kind of automatic object somewhere, you can paste a copy:
DerivedD d = get_some_d(); v.push_back(BasePtr(new DerivedD(d)));
Iteration:
for (BaseContainer::const_iterator it = v.begin(), end = v.end(); it != end; ++it) { (*it)->foo(); }
Update: If you want to initialize an object after building, you can do something like this:
{ DerivedE * p = new DerivedE(x, y, z); p->init(a, b, c); v.push_back(BasePtr(p)); }
Or, if the init function is virtual, even simpler:
v.push_back(BasePtr(new DerivedE(x, y, z))); v.back()->init(a, b, c);
2nd Update:. Here's what the derived object looks like:
struct DerivedCar : Base { enum EType { None = 0, Porsche, Dodge, Toyota }; DerivedCar(EType t, bool a, unsigned int p) : Base(), type(t), automatic_transmission(a), price(p) { std::cout << "Congratulations, you know own a " << names[type] << "!\n"; } } private: EType type; bool automatic_transmission; unsigned int price; static const std::unordered_map<EType, std::string> names;
Usage: Base * b = new DerivedCar(DerivedCar::Porsche, true, 2000);
3rd update:. This is unrelated, just an illustration of how to use lookup tables in favor of switch statements. Suppose we have many similar functions (the same signature) that we want to use based on some integer:
struct Foo { void do_a(); void do_b();
Instead of a switch, we can register all the functions in the lookup table. Here I am assuming C ++ 11 support:
struct Foo { // ... static const std::map<int, void(Foo::*)()> do_fns; void do(int n) { auto it = do_fns.find(n); if (it != do_fns.end()) { (this->**it)(); } } }; const std::map<nt, void(Foo::*)()> Foo::do_fns { { 3, &Foo::do_a }, { 7, &Foo::do_b }, // ... };
Basically, you put static code in container data. This is always a good thing. Now it scales easily; you simply add new features to the search map as they become available. No more touching the actual do() code!