How to create a generic container with unknown member functions?
I noticed that I'm often in the need of a container class. For example when working on a particle system, I create a container class Partic开发者_如何学Pythonles
which has a member vector<Particle*>
. Then I call: Particles* my_particles
like my_particles->draw()
, and in the Particles.draw()
I iterator over the vector<Particle*>
and call draw()
on each of the particles again. The same works for member functions like update()
, addforce()
etc.. Now, I'm working on a project and need a collection of Cube
on which I need to call tween()
, moveTowards()
etc..
I know I can use template, but in the case of a template class the member functions need to be knows before. As I want to check if I can make a generic class, that I can use for example both my Cubes and Particles collections.
Someone who has done this before or can give me some advice on this?
Kind regards, Pollux
The short answer is that you can't do this in c++. You can, however, use STL algorithms and containers to wrap this behavior up.
First, you'd put your Cube or Particle instances into a std::vector
or other container (like you have now).
Then you'd use STL's std::for_each in combination with std::mem_fun.
It'd result in something like this:
std::vector<Particle*> V;
V.push_back(new Particle);
V.push_back(new Particle);
V.push_back(new Particle);
V.push_back(new Particle);
std::for_each(V.begin(), V.end(), std::mem_fun(&Particle::draw));
Not sure I definitely understand, but would the for_each
STL algorithm help? http://www.sgi.com/tech/stl/for_each.html
I read you asking: "can I make a generic container that can be used for both cubes and particles, even though they have different member functions?" Sure, that's the easy part. You can even put both cubes and particles in the same container if you want. If you're dealing with pointers, you just use void*
:
std::vector<void*> objects;
objects.push_back(new Particle(...));
objects.push_back(new Cube(...));
Of course, there's not much you can do with void*
s, except cast them back:
for (i = objects.begin(), i != objects.end(), ++i) {
void* p = objects[i];
Particle* particle = dynamic_cast<Particle*>(p);
if (particle) {
// do particle stuff
continue;
}
Cube* cube = dynamic_cast<Cube*>(p);
if (cube) {
// do cube stuff
}
}
And even if you're only storing Particles in your vector, say, you still have to downcast to work with them:
for (i = objects.begin(), i != objects.end(), ++i) {
void* p = objects[i];
Particle* particle = dynamic_cast<Particle*>(p);
if (particle) {
// do particle stuff
} else {
// error!! I thought someone told me this thing only had Particles...
}
}
You can see that doing this is much more awkward than just storing them in separate vectors where you know the type of each of the objects in each vector and don't have to perform runtime downcasts to work with them. This is why this kind of container is often considered poor style.
Other possibilities for you to look at in this area are boost::any
or boost::variant
, which work on things besides pointers.
As far as I understood, there is a design problem here, you want to iterate over different object categories (Cube/Particle) using the same interface, but it's pretty clear that they couldn't share the same interface, and if you really want that, you must implement the tween() and moveTowards() on the abstract base class of Particles and Cube, and implement nothing in the Particles class.
For completeness, there is also valarray, though using vector/for_each is a better solution.
精彩评论