is bad to use new/delete for huge class?

Started by
48 comments, last by synth_cat 17 years, 9 months ago
Quote:Original post by synth_cat
Beyond that, there must _always_ be some sort of restraint on how many tris or points I can have, because otherwise I could just keep adding geometry 'till my case exploded. This is the main reason I don't see why I need std::vector; even if I use push_back() repeatedly instead of just saying "TRI tris[TRIS_SIZE]", it will always eventually boil down to the same thing. Also, the advantage afforded by std::vector that its contents are stored on the heap can be recreated simply by taking my entire class objects and making them global (which I have just done.)


You have a lot of memory, which will be shared amongst a few things. Why not let them each try to grab what they need, instead of setting a limit on each? Then you only have a problem when the total requirement is more than what "your case" can provide, rather than if *any* set grows beyond what's available.

Depending on how likely it is for any given point to "die" eventually, you might consider an associative container instead of a sequential one (e.g. std::map). This carries more overhead per stored item, but does not use memory for "dead" items.

Quote:
I need to see lots more of the code to help. :s Don't worry, I'm not easily intimidated. :)

Assuming you're just talking about the issue of MapMaker::InitWorld() needing handles to multiple objects, I can simply explain that here without posting code.
...
Because the code works perfectly with arrays and fails with vectors, the problem here has to be some characteristic about std::vector that I don't know about. Do vectors zero themselves out when certain things happen? Is there a certain way you have to get a handle to them for writing to them to work?

Evidently you can't really describe things well enough. We're not psychic; any number of things *could* be going wrong. You might also simply have stumbled upon a test case that wasn't tried out fully with the array version before. These things happen :)

Advertisement
Quote:
Do you mean that my current structure is not aesthetically/stylistically correct, or is there something actually dangerous about it? (The distinction can be hard to determine sometimes :)


Both. Separation of responsibility and isolation improves maintainability; the lack of it makes your code more brittle and prone to error.

Quote:
How so? By the way, I have already taken my InitWorld() and Draw() functions (both members of a controller class which access data of other, minor classes) and converted them to "pass by reference."


Passing a reference as a parameter is not storing a reference. References have a number of properties that make them unsuitable for certain uses -- they cannot be null or invalid, they cannot be reseated once assigned, and they must always be initialized.

Quote:
Beyond that, there must _always_ be some sort of restraint on how many tris or points I can have, because otherwise I could just keep adding geometry 'till my case exploded. This is the main reason I don't see why I need std::vector; even if I use push_back() repeatedly instead of just saying "TRI tris[TRIS_SIZE]", it will always eventually boil down to the same thing.


Not at all. With a fixed-size array you consume X amount of storage even if you need only a handful of that storage for a particular run; this is wasteful. Also, when the day comes that you need X + 1 points in a model, you need to recompile the entire program -- obviously bad. And please don't try to say that TRIS_MAX is 100,000 which should be big enough for every model you ever need, because 64k is most certainly not enough for everybody.

Designing for artificial fixed limits is a crutch. You don't need it.

Quote:
I am currently frustrated with std::vector. Even after rewriting InitWorld() to use pass-by-reference and turning all my big objects into global objects instead of new/delete objects, I still don't see anything on my screen when I render my geometry. InitWorld() is able to write to the vectors, apparently, because I can iterate through std::vector<TRI> tris and see that the right number of .alive values are true, but it is as if my pnts data gets zeroed.

Try using a debugger to verify what and where the actual problem is. What you are suffering from is a symptom of the problem I alluded to above -- your design is brittle, and refactoring (possibly even to a minor extent) renders it a collection of broken, useless bytecodes.

Just from what you've alluded to, I can think of at least of couple ways you can improve the system. Why don't you post more of your code?
Could you please show me how to use std::fill() to set tris or pnts to all NULL? Maybe if I can get that to work I can progress faster with figuring out why my std::vector geometry is invisible.

Thanks!

-synth_cat
Greg Philbrick, Game Developercoming soon . . . Overhauled CellZenith
std::fill(vec.begin(),vec.end(),0);
Thanks a lot, jpetrie! I'll post my further work on this problem as soon as possible.

-synth_cat
Greg Philbrick, Game Developercoming soon . . . Overhauled CellZenith
OK, I just want to ask some more general questions about std::vector.

Let's say that in my game engine I have a dynamic vertex buffer for drawing quads. Every time I want to add a new quad, I do not lock and unlock the actual vertex buffer. Instead I write to an array of vertices (which I call a "proxy vertex buffer"). This array is copied into the actual vertex buffer with a memcpy() command just before the buffer is drawn. My question is, does anyone ever use std::vector for use as a "proxy vertex buffer" in this way?

My second question:

I have to admit that I don't like std::vector that much (it has given me a lot of grief so far.) I also don't really want the functionality provided by push_back() and all that other stuff (suffice it to say that for my purposes it's overkill.) The only real advantage for me in std::vector is that memory in an std::vector is on the heap instead of the stack.

So let's say I have a few objects in my game which contain simple old-fashioned arrays. The objects are declared and constructed within WinMain(). So obviously I have a potential problem with stack-overflow, right?

One way I could fix this would be to convert all the arrays to std::vector. However, I really do not want to do this.

Instead, couldn't I just leave the arrays the way they are and declare the objects as globals? Then the memory would all be on the heap, and I would have everything the way I want it. Would there be any problems with that approach? Bear in mind that none of the objects I'm talking about have anything to do with Windows - they're just d3d and game logic stuff.

Any advice much appreciated!

-synth_cat
Greg Philbrick, Game Developercoming soon . . . Overhauled CellZenith
Quote:Original post by synth_cat
OK, I just want to ask some more general questions about std::vector.

Let's say that in my game engine I have a dynamic vertex buffer for drawing quads. Every time I want to add a new quad, I do not lock and unlock the actual vertex buffer. Instead I write to an array of vertices (which I call a "proxy vertex buffer"). This array is copied into the actual vertex buffer with a memcpy() command just before the buffer is drawn. My question is, does anyone ever use std::vector for use as a "proxy vertex buffer" in this way?


There is no need to use memcpy(). The problem with memcpy is that if, in the future, the data you are copying is not raw binary data, memcpy breaks, and undefined behaviour results.

Here are a number of ways of moving data from a proxy_vertex_buffer to the actual_vertex_buffer:
std::vector proxy_vertex_buffer;...proxy_vertex_buffer.clear();proxy_vertex_buffer.push_back(vertex1);proxy_vertex_buffer.push_back(vertex2);proxy_vertex_buffer.push_back(vertex3);...proxy_vertex_buffer.push_back(vertex55);std::vector actual_vertex_buffer;...// suppose we only want to draw what is in proxy_vertex_buffer:actual_vertex_buffer.clear();actual_vertex_buffer.swap(proxy_vertex_buffer);draw(actual_vertex_buffer);// alternatively, suppose we want to add the contents of// proxy to the actual_vertex_buffer:actual_vertex_buffer.insert(actual_vertex_buffer.end(), proxy_vertex_buffer.begin(), proxy_vertex_buffer.end());// yet another way to do the same thing:std::copy( proxy_vertex_buffer.begin(), proxy_vertex_buffer.end(), std::back_inserter(actual_vertex_buffer) );



Quote:My second question:

I have to admit that I don't like std::vector that much (it has given me a lot of grief so far.) I also don't really want the functionality provided by push_back() and all that other stuff (suffice it to say that for my purposes it's overkill.) The only real advantage for me in std::vector is that memory in an std::vector is on the heap instead of the stack.


I believe you happen to be wrong.

You have a block of memory. You are keeping track of how much of it is in use.

The problem with saying "I want to deal with large blocks of contiguous data, manage the data in the array, and do it on the heap" but "I don't want to use a vector" is like saying "I want to program a game, but I only like programming in BASIC. Tell me how I can write Duke Nukem?"

The right answer to the Duke Nukem question is "learn another language".

In your case, there is a problem with your code if you cannot replace array storage with std::vector storage and cannot tell why it breaks. It means your code is tied together in ways you do not understand, and you have implicit dependancies that are causing problems.

Having large amounts of code that modify large numbers of different data members data is a recipie for brittle code. When you write brittle code, things break, and it is hard to find where the breaks are.

In addition, I would seriously advise doing some debugging. If your output is blank, haven't you tried tracking down where and when in your code things are going wrong?

Here are some reasons why you should use std::vectors over fixed arrays:
1> In debug mode, it will check to make sure you aren't writing past the end/before the beginning of your block of memory.
2> It keeps track of your "high water mark" for you (or the amount of data stored).
3> You can swap the contents of two vectors, very useful for "double-buffer" style management.
4> The memory is on the heap, and automatically deleted for you.
5> Constructors and Destructors are called for you.
6> You get begin() and end() methods, which are really useful when using standard algorithms.
7> You get insert(...)
8> You get push_back(...)
9> std::sort(...), std::remove_if(...), std::unique(...), std::find(...), std::binary_search(...)
10> iterator-loop-semantics
11> Your containers no longer have a hard-cap max -- get more memory, your program can do more.
12> With a bit of work, you can replace your std::vector with a std::list, std::deque, or other standard container, if it turns out they are more applicable.
13> Even if the above are not useful for this current project, as you learn more about std::vector it becomes easier to use. In the future, this will save you lots of effort.

Quote:So let's say I have a few objects in my game which contain simple old-fashioned arrays. The objects are declared and constructed within WinMain(). So obviously I have a potential problem with stack-overflow, right?

One way I could fix this would be to convert all the arrays to std::vector. However, I really do not want to do this.

Instead, couldn't I just leave the arrays the way they are and declare the objects as globals? Then the memory would all be on the heap, and I would have everything the way I want it. Would there be any problems with that approach? Bear in mind that none of the objects I'm talking about have anything to do with Windows - they're just d3d and game logic stuff.


Globals are in the data segment, IIRC, which is different than the heap. But yes, globals are not stored on the stack.
But how would I copy memory from an std::vector to an IDirect3DVertexBuffer*? The actual vertex buffer is not another std::vector.

Greg Philbrick, Game Developercoming soon . . . Overhauled CellZenith
std::copy still works.
I also have a question about std::fill(). Jpetrie, I tried out what you told me and there was a problem. Here's what I did:

//define a struct somewherestruct SHOT{	//simple flags	bool alive;	ShotType type;	//physical dimensions	D3DXVECTOR2 pos;	D3DXVECTOR2 mov;	float height;	float radius;	float tail_length;	float damage; //reflects impact of shot	float life; //from 0 to 1	float wall_loss; //from 0 to 1 - nonfactored by fps_factor	float fade_time; //from 0 to 1 -represents the amount of life the	//shot loses in one second	D3DXVECTOR2 uv; //texcoords};//within class declarationstd::vector<SHOT> shot_types;//within constructorshot_types.resize(NUM_SHOT_TYPES);std::fill(shot_types.begin(),shot_types.end(),NULL);


The resize() command works OK, but there is a problem with the std::fill() command. I do not understand the error message for this command. I have it right here:

..............................................................................

c:\program files\microsoft visual studio 8\vc\include\xutility(2726) : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const int' (or there is no acceptable conversion)
game_structs.h(147): could be 'SHOT &SHOT::operator =(const SHOT &)'
while trying to match the argument list '(SHOT, const int)'
c:\program files\microsoft visual studio 8\vc\include\xutility(2754) : see reference to function template instantiation 'void std::_Fill<std::_Vector_iterator<_Ty,_Alloc>,int>(_FwdIt,_FwdIt,const int &)' being compiled
with
[
_Ty=SHOT,
_Alloc=std::allocator<SHOT>,
_FwdIt=std::_Vector_iterator<SHOT,std::allocator<SHOT>>
]
synthgame.cpp(36) : see reference to function template instantiation 'void std::fill<std::_Vector_iterator<_Ty,_Alloc>,int>(_FwdIt,_FwdIt,const int &)' being compiled
with
[
_Ty=SHOT,
_Alloc=std::allocator<SHOT>,
_FwdIt=std::_Vector_iterator<SHOT,std::allocator<SHOT>>
]

.....................................................................
The problem seems to be with the '0' parameter within the std::fill() call. The problem persisted when I changed it to NULL and to (SHOT)0 (which is undefined, anyway.)


Can you please tell me why I am unable to do std::fill()? Does std::vector::resize() always set everything to NULL on it's own? Even if it does, I still need to be able to use std::fill() because some of my vectors will need to be "cleared" (not resized, but just set to all NULL values) at various points in the game.

Thanks!
-synth_cat

Quote:
std::copy still works.

Even to a Direct3d vertex buffer? Is that unreliable or unsafe? I know that messing with DirectX buffers can be treacherous.
Greg Philbrick, Game Developercoming soon . . . Overhauled CellZenith

This topic is closed to new replies.

Advertisement