Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

boost::ptr_vector and class inheritance help

Started by JimmyDeemo Mar 2, 2009 at 4:17 AM 3 replies 1.4k views
Original Post
JimmyDeemo
JimmyDeemo
Ok so i have a little bit of a problem regarding using boost's ptr_vector and performing an operation in order for me to get somthing sorted on my game. Its probably a problem with how i'm going about it so if someone could make some suggestions it would be much appreaciated. Ok here goes, firstly i have my base class and my derived class;

class CBaseEnemy
{
	public:
               ///...stuff here

		virtual void Update( D3DXVECTOR3 target, float delta );

               ///...stuff here	
	private:
               ///...stuff here
};

class CRabbit : public CBaseEnemy
{
	public:

		CRabbit(D3DXVECTOR3 startPos);
		virtual ~CRabbit(void);

		void SetPlayersDir( D3DXVECTOR3 dir ) {m_playersDir = dir;}
		void Update( D3DXVECTOR target, float delta );

	private:

		D3DXVECTOR3 m_playersDir;
};





Now for one reason or another the a rabbit object needs to know the direction the player is facing. I would normally pass this into an update function, but because the update function is inherited i can't (or can i!?) change it's parameters. My enemies are stored in a boost::ptr_vector declared and filled like this;

class MainGame
{
    //....
    private:

    CBaseEnemy* GetNewEnemy( eMainGameSpriteIDs enemyID = eInvalidId );

    //....

    boost::ptr_vector<CBaseEnemy> m_pEnemies;
};

CBaseEnemy* CMainGame::GetNewEnemy( eMainGameSpriteIDs enemyID )
{
	CBaseEnemy* newEnemy;

	switch( enemyID )
	{
		case eChicken:
			newEnemy = new CChicken( GenEnemyStartPos() );
			break;
		case eRabbit:
			newEnemy = new CRabbit( GenEnemyStartPos() );
			break;
		case eCow:
			newEnemy = new CCow( GenEnemyStartPos() );
			break;
		case eLlama:
			newEnemy = new CLlama( GenEnemyStartPos() );
			break;
	}

	return newEnemy;
}

//In MainGame::Update()
if( m_spawnTimer.Expired( 1000 ) )
{
	m_spawnTimer.Restart();
	m_pEnemies.push_back( GetNewEnemy() );
}




I am also cleaning up the enemy vector with using stl algorithms;

bool EnemyPred( CBaseEnemy e )
{
	//TODO: Make the time a static const in the bullet class.
	return e.IsDead();
}

//In maingame::Update
if( m_pEnemies.size() > 0 )
{
	m_pEnemies.erase( std::remove_if( m_pEnemies.begin(),m_pEnemies.end(),EnemyPred ), m_pEnemies.end() );
}



Almost there now people, thanks for sticking with me this far. In order to set the player dir in the rabbit objects i do this;

boost::ptr_vector<CBaseEnemy>::iterator itor;
for( itor = m_pEnemies.begin(); itor < m_pEnemies.end(); ++itor )
{
	//This seems to cause memory issues. Maybe smart pointer?
	//Special cases dependant on enemy type
	switch( itor->SpriteId() )
	{
		case eRabbit:
			//Set the players dir
			CRabbit* rabbit = static_cast<CRabbit*>(&(*itor));
			rabbit->SetPlayersDir( m_player.GetPlayerDir() );
			break;

	}

	itor->Update( m_player.Position(), fDelta );
}



Now to me that make sense but i'm sure its very wrong. Anyway with the above code in i get an assertion that seems to emenate from where i am cleaning up the enemy list. I'm not sure but it seems to be something related to deleting a pointer that is not there anymore. Ok so what if someone could point some things out that would be good. Is this a problem with how i'm using pointers and containers? i.e. is some of the above just stupid Is this a design problem? i.e. How could i change me classes to not have this issue of needed to check the type of the object and cast it to set the direction. Any help much appreciated. Thanks. [Edited by - JimmyDeemo on March 2, 2009 6:20:48 AM]
xylent
xylent
Hi,

I haven't used the containers of boost library before but it seems to me that there is a chance that the contents of the container may change while you are looping through the items in the container using the iterator. This is something you need to avoid in your code I think. When using an iterator, if the contents of the container changes then that iterator becomes obsolete. I'm not sure if this is related to your problem though.

Take care,
JimmyDeemo
JimmyDeemo
Thanks for a reply. I will check that althought i'm pretty sure that i'm not adding or removing elements from my container while i'm iterating through them.

Perhaps i'm over complicating things, let me ask a more direct question;

If i want to access a non virtual member function from a CDerived object, but i only have a CBase pointer, how do i do it?

Then if i have a boost::ptr_vector of CBase elements, how can i access a non virtual member function of a CDerived object, that could be in the vector.

I think there is a better way to do what i have done above with dynamic_cast, but not sure if it will fix my problem.
JimmyDeemo
JimmyDeemo
Ok well i seem to have got rid of my issue. I used dynamic_cast to see if the object was a Rabbit, if the pointer wasn't 0 i knew the object was a Rabbit enemy.

CRabbit* rabbit = dynamic_cast<CRabbit*>(&(*itor));		//Rabbit will be 0 if itor is not pointing to a rabbitif( rabbit ){	rabbit->SetPlayersDir( m_player.GetPlayerDir() );}


Having read up on the use of dynamic cast it seems that this is pretty much what it was meant for. But why was my first idea causing memory problem? Anyone know or wish to fathom a guess?
rip-off
rip-off
An alternative might be to use something like the observer pattern. I.e. on creation, the rabbit instance registers itself as an observer with the player instance. For this the player would be passed to the Rabbit constructor.

Every time the player changes direction, it simply loops over the observers that have registered with it and notifies them that its orientation has changed.

This way the rabbit and player can communicate without involving the MainGame class - except during the creation of a rabbit instance, but that is the only time you know the concrete types involved.

Boost has many classes (boost::bind, boost::function and the signals library) which might be helpful. I can't say more than that though, I use Lua for this sort of thing.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.