Click to See Complete Forum and Search --> : getting all members of a base class from an array


Mikau
March 5th, 2008, 11:28 AM
here is a problem I was unable to find a good solution for in Java. I'm wondering if there's a way you can do it in C++.

Suppose you have a game thats divided into rooms, and each room has an array of pointers to all the objects in the room. Each object in the room is a subclass of GameObject, and so the room has a GameObject pointer array

Now suppose you use inheritance to organize your objects into different groups, such as SolidObject (objects you can't walk through) Enemies, Projectiles, items, etc. and suppose you want to get all the objects in the room (contained in the GameObject array) that are members of one of those groups (all instances of that class, and its subclasses).

I would be nice to write a function to do this, you basically need to check each item in the array to see if it is an instance of the class you are looking for. The only problem is, the mechanism that allows you to do this, namely 'instance_of' in java, 'dynamic_cast' in C++, both take a class name as an argument, and class names (to my knowledge) cannot be passed as an argument to a function. Therefore, every time I need to get all the members of a base class from the array, i have to write a for loop right then and there so I can explicitly give the class name with the cast operation. This is how I was forced to do it in java. Is there a better way this can be done in C++?

GNiewerth
March 5th, 2008, 11:46 AM
Take a look at the visitor pattern (http://en.wikipedia.org/wiki/Visitor_pattern). Using this design pattern you can identify objects by method overloading (itīs also called double dispatching).


class IVisitor
{
public:
IVisitor();
virtual ~IVisitor();

virtual void visit( MonsterObj& obj ) = 0;
virtual void visit( TreasureObj& obj ) = 0;
};

class GameObj
{
public:
GameObj();
virtual ~GameObj();

virtual void accept( IVisitor& Visitor ) = 0;
};

class MonsterObj : public GameObj
{
public:
MonsterObj();

void accept( IVisitor& Visitor )
{
// 2nd step of double dispatching
Visitor.visit( *this );
}
};

class TreasureObj : public GameObj
{
public:
TreasureObj();

void accept( IVisitor& Visitor )
{
// 2nd step of double dispatching
Visitor.visit( *this );
}
};


class MonsterFilter : public IVisitor
{
std::vector<MonsterObj*> m_Monsters;

public:
MonsterFilter( const std::vector<GameObj*>& Objects )
{
for( const std::vector<GameObj*>::const_iterator it = Objects.begin();
it != Objects.end(); ++it )
{
// 1st step of double dispatching
it->accept( *this );
}

void visit( MonsterObj& Obj )
{
// insert address of monster into vector
m_Monsters.push_back( &Obj );
}

void visit( TreasureObj& Obj )
{
// ignore treasure objects, weīre interested in monsters only
}

std::vector<MonsterObj*>& get_monsters()
{
return m_Monsters;
}
};

int main()
{
std::vector<GameObj*> AllObjects;

// filter all game objects for monster type objects
MonsterFilter Filter( AllObjects );

// now MonsterFilter.get_monsters() returns an vector of pointers to
// all monster objects of AllObjects
}


This technique is preferrable over dynamic_casts, because it helps building more robust applications. Each time you add a new game object you have to search your whole code and add an additional comparison branch wherever itīs needed. The compiler wonīt complain if youīre missing a handler for the new object type, so you will most likely notice your application does not behave the way itīs supposed to. If things get even worse this branch is executed very rarely, so you donīt notice it until your application has been shipped to a customer.
When using the visitor patter you have to modify the IVisitor class by adding a new pure virtual method for the new object type. Now the compiler complains every time it tries to compile a IVisitor-derived class because itīs missing the new method.