Jump to content
  • Advertisement
Sign in to follow this  
Juliean

Resizing vector correctly

This topic is 2951 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

I'm storing all the tiles of my tileset in a vector, and as I want to save memory I want that vector to be resized automatically if a new tile is added. My code looks like that:

void CTileSet::SetTile(LPCWSTR FileName, int ID, int PosX, int PosY, int PosZ)
{
if(m_Tiles.size() <= ID)
{
m_Tiles.resize(ID + 1);
}
m_Tiles[ID].Set(FileName, ID, PosX, PosY, PosZ);
}



m_Tiles is my vector, and it doesn't work at all. The thing is, if the ID of the new tile is greater than the actuall size of the vector, the vector should be resized to fit the ID. But I'm doing something wrong, as I get for example the "vector subscript out of range"-error every time if I correct the "<=" in the if-condition to "=", and some other errors when leaving it like it is. Any correct solutions?

Edit: The problem seems to be that I've got a vector for sprites, too.
The full code looks like that:
void CTileSet::SetTile(LPCWSTR FileName, int ID, int PosX, int PosY, int PosZ)
{
if((int)m_Tiles.size() <= ID )
{
m_Tiles.resize(ID + 1);
m_Sprites.resize(ID + 1);
}
m_Tiles[ID].Set(FileName, ID, PosX, PosY, PosZ);
m_Sprites[ID].Create(m_lpDevice, FileName);
m_Sprites[ID].SetPosition(PosX, PosY);
m_Sprites[ID].SetZ(PosZ);
}


When using that code, I get an excerpt in the lines

CSprite::~CSprite(void)
{
if(NULL != m_lpSprite)
{
m_lpSprite->Release();
m_lpSprite = NULL;
}
}


saying something that an unhandelt excerption occuredt for the line m_lpSprite->Release. So said, m_Tiles is an object

vector<CTile> m_Tiles;

and

vector<CSprite> m_Sprites;

Seems that the destructor is called every time the vector is resized, but actually this just appears if I add more than 3 Tiles. Really strange, can't get the solution..

Share this post


Link to post
Share on other sites
Advertisement
First, posting code that works isn't as good as posting code that doesn't work for trouble-shooting purposes. We have to guess what the incorrect code looks like.

Second, it would seem that:

if( m_Tiles.size() <= ID )

is the way to go. That will work if vector.size()==10 and ID==12. Otherwise you have a problem.

In any case,

if( m_Tiles.size() = ID )

tries to set a vector function to a value.

Do you mean to use:

if( m_Tiles.size() == ID )

Share this post


Link to post
Share on other sites
Quote:
Original post by The King2
Seems that the destructor is called every time the vector is resized, but actually this just appears if I add more than 3 Tiles. Really strange, can't get the solution..


Vector's internal storage is larger or equal to number of elements. Whenever an array is resized, if there isn't enough room, new, larger storage is allocated, old elements are copied to new location, old elements are destroyed (hence the destructor call) and new storage is set as vector's internal storage.

Apparently, in above case, the default size for vector's storage is 3 or 4.


To add item to vector, use push_back.

Tile t;
t.Set(FileName, ID, PosX, PosY, PosZ);
m_Tiles.push_back(t);

Any object added to vector may be copied as needed, and copying may involve creating temporaries, so destructor will be called multiple times during object's life time.

Any object to be used with standard library containers must properly implement Rule of three.

As such, storing raw pointers without reference properly implemented reference counting semantic isn't possible.

Share this post


Link to post
Share on other sites
@Buckeye: Hm, ok, well actually none of the both codes is working, every one creates another excerpt.

Actually I don't get why this is is going to be a problem?
Why should

if( m_Tiles.size() <= ID )

only work with vector-size of 10 and ID of 12? umm...

if( 10 <= 12) //true, resize
if( 11 <= 12) //true, resize
if( 9 <= 12) //true, resize
if( 13 <= 12) //not true, but no need to resize

Seems that I don't get your point.

Yes I know its "==" but it rather meaned just "<".

@Antheus: Ah, thanks, I see, so far, but it somehow doesn't work either...
What am I doing wrong?

void CTileSet::SetTile(LPCWSTR FileName, int ID, int PosX, int PosY, int PosZ)
{
if((int)m_Tiles.size() <= ID )
{
CTile TempTile;
CSprite TempSprite;
TempTile.Set(FileName, ID, PosX, PosY, PosZ);
TempSprite.Create(m_lpDevice, FileName);
TempSprite.SetPosition(PosX, PosY);
TempSprite.SetZ(PosZ);
m_Tiles.push_back(TempTile);
m_Sprites.push_back(TempSprite);
}

}


Just gives me the excerption from the destructor now. Maybe I somehow didn't get your point? Or do I badly need to stick to the rule of three? I see that it is important but I hope it works without, too. Or am I wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by The King2
Or do I badly need to stick to the rule of three?
Yes. If you need one of a copy constructor, assignment operator, or a destructor you probably need all 3. How large are CTile and CSprite? What do they actually do?

An alternative would be std::vector< boost::shared_ptr<X> > or boost::ptr_vector<X> where X is CTile or CSprite. Specific advice depends heavily on what CTile and CSprite are primarily used for.

Share this post


Link to post
Share on other sites
@nobodynews:

CSprite is a class just for handling a complete sprite (including the sprite itself, the texture, the position, etc..). I use it for every piece of grafic I want to display (except if I want to use a Surface), careless what functionality it has.
Thats everything stored inside:

LPDIRECT3DDEVICE9 m_lpDevice;
LPD3DXSPRITE m_lpSprite;
LPDIRECT3DTEXTURE9 m_lpTexture;
D3DXVECTOR3 m_vPosition;
D3DXVECTOR2 m_vScale;
D3DXVECTOR3 m_vCenter;
float m_Rotation;
int m_Width;
int m_Height;
LPCWSTR m_FileName;



It's not that long code by now, just basic stuff to set it up, or to change the grafic, or to move it around, later on it will get some more functionallity to allow me to animate it, but thats it.

CTile isn't a problem so far, it just stores some basic information about the Tile itself:

LPCWSTR m_FileName;

int m_ID;
int m_PosX;
int m_PosY;
int m_PosZ;

int m_Width;
int m_Height;



The sprite for the tile is stored externally in a special sprite-set. That class doesn't do much more than to represent the tile, and to allow me to access information about the tile even if it changed (for example this contains the basic possition independend of the screens position). Not much more about that.

Well, what do you say about that, or what do you advice me? So far I'm trying to write a copy constructor but have little problems with that:

Ok, the constructor I tries looks like that:

CSprite::CSprite(const CSprite& rhs)
{
m_lpSprite = rhs.m_lpSprite;
m_lpTexture = rhs.m_lpTexture;
m_lpDevice = rhs.m_lpDevice;
m_vPosition.x = rhs.m_vPosition.x;
m_vPosition.y = rhs.m_vPosition.y;
m_vPosition.z = rhs.m_vPosition.z;
}



So, it works basically, but it doesn't change anything, because I need a const and the Sprite isn't one. When removing the "const" from the constructor my compiler tells me the the CSprite class hasn't got any copy constructor or it is defined "explicite". Any solution here or any good tutorial about copy constructor? I understand the basics but somehow can't use it.

Edit: Oh, and the thing about boost::shared_ptr : what file do I have to include to use that?

Share this post


Link to post
Share on other sites
Quote:
Original post by The King2
So, it works basically, but it doesn't change anything, because I need a const and the Sprite isn't one. When removing the "const" from the constructor my compiler tells me the the CSprite class hasn't got any copy constructor or it is defined "explicite". Any solution here or any good tutorial about copy constructor? I understand the basics but somehow can't use it.
You should use const as that's the only way to actually make a copy constructor. It doesn't mean "the constructor is called only when you pass a constant CSprite" it means "the constructor is called whether or not your CSprite is const". So this will work fine:
const CSprite a;
CSprite b;
CSprite new_a(a);
CSprite new_b(b);


Additionally, why do you store the filename in the CSprite and CTile? At least use a std::wstring!

The quick answer to solve your immediate problem would be to actually use the reference counting built into the COM interfaces you are using for LPD3DXSPRITE and LPDIRECT3DTEXTURE9. In the constructor you would call AddRef on m_lpSprite m_lpTexture and probably m_lpDevice. You would probably want to check the result of Release in the destructor as well.

A comment on your desctructor: first, why would m_lpSprite or m_lpTexture be NULL? The sprite should be valid from constructor onward so the destructor should be pointing to all valid objects. Also, if the object is being destroyed why do you need to set the pointers to NULL? They're just going to not exist in 1 more statement anyway.

Those two points aside, The general philosphy is to have a pool of resources and you assign those resources to your tiles and sprites as necessary. The pool will keep all resources you are using available for when they are needed. Of course, with this pool you probably need to be able to find those resources again based on some sort of identifier... like a filename or a number. And we can associate those two things with each other using a map (there are other options but this is probably the easiest):
std::map<std::wstring, LPD3DXSPRITE> pool_sprites;
std::map<std::wstring, LPDIRECT3DTEXTURE9> pool_textures;
Add sprites (and textures) like this:LPD3DXSPRITE new_sprite = FunctionThatGetsYouASprite(fileName);
pool_sprites.insert( std::make_pair(fileName, new_sprite) );
And in your sprite class:

class CSprite {
// Member variables:
...
// Create sprite immediately instead of delaying creation later on
CSprite(std::wstring filename,
LPDIRECT3DDEVICE9 device /* why do you need this in your Sprite? */,
LPD3DXSPRITE sprite,
LPDIRECT3DTEXTURE9 texture,
D3DXVECTOR3 position,
D3DXVECTOR2 scale,
D3DXVECTOR3 center,
float rotation,
int width,
int height) {
// initialize non-pointer variables
if(device == NULL ) {
// device shouldn't be null! Throw an exception
}
if(sprite== NULL) {
// sprite shouldn't be null! Throw an exception
}
if(texture == NULL) {
// texture shouldn't be null! Throw an exception
}

m_lpDevice = device;
m_lpDevice->AddRef();

m_lpSprite = sprite;
m_lpSprite->AddRef();

m_lpTexture = texture;
m_lpTexture->AddRef();
}
~CSPrite() {
m_lpDevice->Release();
m_lpSprite->Release();
m_lpTexture->Release();
}
};
I'm not terribly happy with this as an example but I've spent way too long on this today already. Perhaps it will help you find a better design or someone else will think of something better.

As for your final question: shared_ptr is in boost which you can download here. You don't have to build boost to use the smart pointers. The headers for the smart pointers you need are listed here. If you are using a recent compiler like Visual C++ 2010 then you just include <memory> and use std::tr1::shared_ptr (see here

Share this post


Link to post
Share on other sites
Ah, I see, I misunderstood the copy constructor a bit, thought it would work like that:

const CSprite a;

CSprite b;

a = b;

Just like the compiler would automatically use the copy constructor or something, how stupid of me.

Thought that isn't satisfactory a possible solution for my problem which I currently used was the function reserve(), which prevents the vector of deleting the objects and re-creating them, because it reserves a certain value of memory, but size() still return the actual value of elements. So:

m_Sprites.resize(1)
m_Sprites.reserve(999)

Has 1 element but keeps memory ready for 999, quess you already know that. Is this actually very memory-intensive or not? I'm creating a copy constructor, indeed.

hm, can you or someone explain that std::wstring? Havn't heard of that before, maybe I find a nice tut.

Quote:
The quick answer to solve your immediate problem would be to actually use the reference counting built into the COM interfaces you are using for LPD3DXSPRITE and LPDIRECT3DTEXTURE9. In the constructor you would call AddRef on m_lpSprite m_lpTexture and probably m_lpDevice. You would probably want to check the result of Release in the destructor as well.


Hmm ok I'm taking a look at that, not so sure now but I quess I'll understand how to do that.

The check if the Sprite or the Device is NULL depends on how my classes works, I'm not passing the device and creating the sprite via constructor but with an additional-setup function. That as well as the pointer set to NULL aren't my Ideas, I've got that from a book about game-developement, dunno, quessed that was the way it goes till now.

Yep, I already know, theres a whole chapter about a resource-manager in my book, thanks anyway, I'm going to implent that as soon as I've got the basic features.

Ok, I'm going to checkt that boost and shared_ptr, as well as trying to finish my copy constructor, I've already got some problem with that, but I'm going to write this tomorrow, now I'm going to sleep. Thanks so far.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!