Iteration over every object in a class
Due to the nature of a game I am making, I need to be able to create objects at random on the fly. I need to be able to change the开发者_开发技巧 variables of all currently existing objects of a class 60 times a second.
I've been told that although C++ is incapable of reflection, I would be able to pass the addresses of objects to a vector. Unfortunately, I'm just a little bit confused as to how to do that. Is there a better solution? If not, can someone provide example code?
tl;dr? I need to run a function on the variables of all the objects in a class. Howdo?
"Pass the address of an object to a vector" sounds like this
std::vector<MyObject *> objectAddresses;
This is a vector of pointers to MyObject. Note that you have to add and remove the pointers manually. Failure to remove an object's pointer from this vector when it is deleted can easily crash your program.
Depending on performance needs, you may want to choose an STL container other than vector. Vectors will make your iteration very fast, but removing an object from an arbitrary position in the vector will be expensive. Without first knowing its position in the vector, it will also be difficult. Consider std::set.
http://www.cplusplus.com/reference/stl/ lists the containers provided with stl, and roughly describes their performance.
Once you have your container of pointers, simply for-loop over each entry and call the function. You will probably need to dereference twice e.g. (*itor)->func().
Maybe it's just your terminology, but I have more than one object, and I'm confused as to how this would work. Mind going further into depth? I'm thinking it should be more along the lines of std::vector objectAddresses;
If you declare class MyClass
, you should say vector<MyClass*>
. If you declare class MyObject
, say vector<MyObject*>
. It will be the same to the compiler, so use whichever is more readable to you.
The std::vector is not what most graphical programmers think of as a vector (e.g. 3 values signifying a position in 3d space). It is a self-resizing array. So as you add values to a vector (through the push_back method) it will check to see if it has allocated enough memory to hold the additional values, and re-allocate a larger block of memory if necessary.
class MyClass
{
public:
static std::vector<MyClass *> objAddrs;
MyClass()
{
objAddrs.push_back(this);
}
~MyClass()
{
// Find this in objAddrs & objAddrs.erase()
}
void doWork()
{
// something useful here
}
};
Will cause every instance of MyClass to add its own address to the end of objAddrs at construction. Remember that the destructor must remove that same address!
for(std::vector<MyObject *>::iterator itor=MyClass::objAddrs.begin(); itor!=MyClass::objAddrs.end(); ++itor)
{
(*itor)->doWork();
}
Will call the doWork() function on every existing instance of MyObject(). If any MyObjects have been destroyed & the destructor fails to remove them, a dangling pointer will be dereferenced & undefined behavior will be invoked (probably a segfault).
std::set lets you remove by value:
class MyClass
{
public:
static std::set<MyClass *> objAddrs;
MyClass()
{
objAddrs.insert(this);
}
~MyClass()
{
objAddrs.erase(this);
}
};
I assume that you want to get a way to iterate over all instances of a
class that are currently in existence without going through the hassle
of managing the storage yourself. SelfStore
is intended as a base
class for such functionality. It uses a list
internally because I
assume that objects are created and deleted often. You might want to
switch to another storage type that suits your usecase better. The
iterator is kept as a member for fast removal but could be done
without if the extra storage is a problem. This still needs some
(alot) tweaking, e.g. private storage and a static begin
end
, no
mutable access, hiding of the storage type through typedefs etc. It is
not threadsafe. SelfStore
is only a template because it might come
in handy.
#include <iostream>
#include <list>
template<typename T>
class SelfStore
{
typename std::list<SelfStore*>::iterator it;
public:
SelfStore() {
store.push_back(this);
it = --end(store);
}
virtual ~SelfStore() {
store.erase(it);
}
static std::list<SelfStore*> store;
};
template<typename T>
std::list<SelfStore<T>*> SelfStore<T>::store = std::list<SelfStore<T>*>();
struct Foo : public SelfStore<Foo> {
};
int main()
{
{
Foo f, g, h;
std::cout << SelfStore<Foo>::store.size() << std::endl;
}
Foo a, b;
std::cout << SelfStore<Foo>::store.size() << std::endl;
return 0;
}
In the constructor of your objects add them to a global, static list of objects (remember to remove the object in the destructor!). That will let you find the objects and manipulate them. If your program is multithreaded you will need a way to sychronize access to the list and the object you are manipulating.
Unless you have your objects in a container, you can't use a standard iterator or iteration loop over your data members.
I suggest creating a visitor
method that takes a functor and applies it to each data member.
Search Stack Overflow for Visitor design pattern.
精彩评论