PDA

View Full Version : C++ Question: Passing dynamic arrays (world's objects)



BradlyP
12-03-2000, 10:49 AM
This isn't specifically OGL related, but I think it's relavent. I'd sure appreciate some help. I've been out of C/C++ coding for a while, and the pointer/reference stuff is still a little fuzzy.

Here's my problem: I have defined a structure to hold an "object" in my virtual world. Then, in my main() function, I define a dynamic array to hold these objects (so I can define at runtime how many objects there are). Next, I want to pass my whole dynamic array (well, a pointer to it) to another function so that I can process the whole list of objects (like initializing the world or drawing the scene).

Here's a simplified version of my code to illustrate where I'm having the problems (where you see the question marks), I'd really apprecieate it if someone could look it over and give me some guidance:



typedef struct Object_typ {
GLfloat matrixArray[16];
int objectID;
int state;
} Object, *Object_ptr; // ??? Should I use the "*Object_ptr" type, or is this confusing (in general)?

Object* controlledObject; // Pointer to the Object our control inputs affect
Object* cameraObject; // Pointer to the Object that is the current "camera"


void initWorldData(???What do I put here??? worldObjects)
{
int totalObjects = 3;
delete [] worldObjects; //??? is this correct? can I do this beofer worldObjects has been allocated w/ new???
worldObjects = new Object_ptr[totalObjects]; // pointer to dynamic array of pointers to objects

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
for (int index = 0; index < totalObjects; index++){
// ??? is this the correct syntax to reference the object's matrixArray???
glGetFloatv(GL_MODELVIEW_MATRIX, worldObjects[0]->matrixArray);
}

cameraObject = worldObjects[0]; // Pointer to the Object that is the current "camera"
controlledObject = worldObjects[1]; // Pointer to the Object our control inputs affect
}

int WINAPI WinMain() {
//??? which of the following two lines should I use???
Object* (*objects); // pointer to dynamic array of pointers to objects
Object_ptr (*objects); // pointer to dynamic array of pointers to objects

initWorldData(objects); // ??? is this the correct way to pass a dynamic array of structs???
}



Thanks a bunch!
-BradlyP

[This message has been edited by BradlyP (edited 12-03-2000).]

Michael Steinberg
12-03-2000, 10:59 AM
Generally, I would think

object_t *object

will be the most readable style, or use something like LP like some windows things do, however, I prefer the first way.

So for your world, that would be
object_t **object = (object_t**) malloc( num_objects*sizeof(object_t*));

And your function to work on it would look like:

void work_on_it( object_t **objects )

That to theory of what you want to do.
For my purposes, I always use trees, perhaps that's a habbit of me, but it works allright. It doesn't cost as many operations to add a certain object to the end, since they don't need to have saved indexes linearly. also, if you would want to add an object to the middle of the tree, that would result into a great memmove or memcpy.

Deiussum
12-03-2000, 01:53 PM
If you need to actually change the pointer, you could do your function one of two ways. You could have a pointer to a pointer like this...

void Somefunction(object** obj, int size)
{
*obj = new object[size];
}

called like so..

object* objects;
SomeFunc(&objects, someSize);

Or you could do use a reference to a pointer like so...

void SomeFunction(object* &obj, int size)
{
obj = new object[size];
}

Which would just be called like this.

objects* objects;
SomeFunction(objects, someSize);

If you don't need to change the pointer itself (like with allocating memory for a new array), you can just pass the pointer and a size of the array to use to make sure you don't read past the end of the array.

If a pointer hasn't been initialized with new, you shouldn't use delete on it or you could run into problems. Most likely this would result in an exception error.

BradlyP
12-03-2000, 02:06 PM
Michael,

Thanks a bunch for the help... you pointed me right in the direction I needed to go. I prefer to use new/delete instead of malloc/free... I compile my projects as C++ because it forces me to be a little more deliberate for some things, and I've heard that new/delete will provide better memory management that malloc/free (but what do I know?).

I figured out what I was doing wrong. See, I was trying to duplicate functionality that I had in a program I wrote years ago (when I was more comfortable w/ pointers and their syntax). The part I missed is that I don't want to allocate memory for an array of objects (structs), I want to allocate memory to hold pointers to all the objects in my world, and then allocate the objects one at a time. So, I was doing this:




void initWorldData(Object_t **worldObjects) {
int totalObjects = 3;
worldObjects = new Object_t*[totalObjects];
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGetFloatv(GL_MODELVIEW_MATRIX, worldObjects[0]->matrixArray);

I was skipping the step of actually allocation the memory for the object:



void initWorldData(Object_t **worldObjects) {
int totalObjects = 3;
worldObjects = new Object_t*[totalObjects];
worldObjects[0] = new Object_t[1];
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGetFloatv(GL_MODELVIEW_MATRIX, worldObjects[0]->matrixArray);


I could use a tree, but the dynamic array method also allows me to add as many objects as I like (one at a time) as long as I've allocated enough room in my pointer array to hold the pointers to the objects. If I set a maximum of 5,000 objects, it would use up 20,000 bytes, even if I only use one. I can live with that -- RAM is cheap http://www.opengl.org/discussion_boards/ubb/smile.gif.

The reason I prefer this method is that it is much faster to loop through all the objects in my world, or go to a specific object, with the array. I don't have to keep careful track of where I am, or use recursion (which, at my current skill level scares me http://www.opengl.org/discussion_boards/ubb/eek.gif ) :



for (int index = 0; index < maxObjects; index++) {
if (worldObjects[index]) { // check for null pointer
processObject(worldObject[index]);
}
}

If I want to re-order my objects, I just have to re-order the pointers to them, not the whole objects. But, of course, trees are more versitile, and you can derive relational info about the world from the layout of the tree, assuming it's not just a simple linked-list.

Thanks again,
-BradlyP

BradlyP
12-03-2000, 02:38 PM
Deiussum,
Thanks for the reply!


Originally posted by Deiussum:
If you need to actually change the pointer, you could do your function one of two ways. You could have a pointer to a pointer like this...

void Somefunction(object** obj, int size)
{
*obj = new object[size];
}

Assuming size > 2, after doing the above would the following be valid to change my second object?

obj[1]->someObjectMember = somethingElse;


called like so..

object* objects;
SomeFunc(&objects, someSize);

Hmmm.... now this confuses me somewhat... wait a second... OK, I think I get it now. I just need to get myself more comfortable with pointers (again).


Or you could do use a reference to a pointer like so...

void SomeFunction(object* &obj, int size)
{
obj = new object[size];
}

OK, that "object* &obj" really confuses me... it's making my head hurt http://www.opengl.org/discussion_boards/ubb/confused.gif. I'm going to come back to this post after I've fiddled around with pointers some more and see if I can figure that one out.


If a pointer hasn't been initialized with new, you shouldn't use delete on it or you could run into problems. Most likely this would result in an exception error.

That makes sense. Would it be good enough to just check it for nullity before deleting it (could this possibly lead to memory leaks?), or should I make a flag variable to indicate that the memory has been allocated? I suppose if I had a flag that said that the memory was allocated, but it was in fact a null pointer, I shouldn't call delete anyway, so a flag doesn't really make sense... right?

Thanks for the help!
-BradlyP

Sven Clauw
12-03-2000, 02:38 PM
First of all: NEVER deallocate memory without first allocating it.
So no delete or delete [] before any new or new[].
Second: With (Object**)malloc(size*sizof(Object*)) or objects = new Object_ptr[size] you only allocate memory for the array of pointers. If you want to use the objects (in the for-loop), you'll first have to allocate memory for those objects. Thus before using them, do objects[i] = new Object(...) or objects[i] = malloc(sizeof(Object)).

About the relevance of this post: this discussionboard is for OpenGL beginners, not C++ beginners. Before trying to make 3D-apps (either OpenGL or D3D), make sure you have a very good knowledge and understanding of the language you are programming in. 3D is one of the most challenging terrains for a programmer.
I'd suggest reading a good C++ manual before starting.
Please don't feel offended by what i'm saying here, but you will save a lot of time you can use to think about the 3D aspect of the program, rather than the actual programming. In general, the programming should be the most easy part of creating 3D apps.

with kind regards,
Sven
Sven.Clauw@student.kulak.ac.be

Sven Clauw
12-03-2000, 02:45 PM
Another thingy :
Checking for null-pointers is a good idea, but keep in mind that when you do not allocate memory for an object, it doesn't mean that the pointers points to 0x0000000 (the null-pointer). So when you allocate your array of pointers, set each pointer in the array to NULL, before doing anything else.
In fact, you should do this all the time when you use dynamic allocation.
Only then can you check for null-pointers.
I know this requires a little more work, but it will avoid many bugs later on.

Sven

Deiussum
12-04-2000, 08:09 AM
Originally posted by BradlyP:
OK, that "object* &obj" really confuses me... it's making my head hurt . I'm going to come back to this post after I've fiddled around with pointers some more and see if I can figure that one out.


Passing parameters by reference is one of those things you can do in C++ but not C. Basically, it's similar to passing by pointer, but you don't specifically pass the address when you call the function. For instance...

int Blah(int &n)
{
n = 10; // don't have to treat n as a pointer
}

int c = 5;
Blah(c); // c now equals 10.

If you did the same thing as a pointer, you would have to treat it as a pointer in the function and for calling the function, you'd have to pass the address of c (i.e. Blah(&c))



Originally posted by BradlyP:

That makes sense. Would it be good enough to just check it for nullity before deleting it (could this possibly lead to memory leaks?), or should I make a flag variable to indicate that the memory has been allocated? I suppose if I had a flag that said that the memory was allocated, but it was in fact a null pointer, I shouldn't call delete anyway, so a flag doesn't really make sense... right?


As Sven said, when you first create the pointers they won't be automatically NULL unless you explicitly set them to NULL. It's good practice to do this. The same goes for other local variables that do not get explicitly initialized.

void SomeFunc()
{
int n; // n is probably garbage until initialized
int *ptr; // ptr probably points to garbage until initialized
}

MikeC
12-04-2000, 09:09 AM
Just a tip - Sven et al are correct in saying that you should always initialize unallocated pointers to null, but if you do this you don't actually need to check them on deletion. The C++ language spec guarantees that calling delete on a null pointer is a harmless no-op.

Of course, you do need to remember to set the pointer to null once you've deleted it. One more reason to use smart pointers instead.

Deiussum
12-04-2000, 09:49 AM
Originally posted by MikeC:
Just a tip - Sven et al are correct in saying that you should always initialize unallocated pointers to null, but if you do this you don't actually need to check them on deletion. The C++ language spec guarantees that calling delete on a null pointer is a harmless no-op.

...

Yeah, I guess I knew about the no-op for deleting NULL pointers, but for some reason, I tend to do checks before deletes anyway. http://www.opengl.org/discussion_boards/ubb/smile.gif

Michael Steinberg
12-04-2000, 12:43 PM
Hey guys, just in case: It's obvious that if you allocate memory for an array of pointer to a structure you don't allocate memory for the structures/objects. I thought that everybody would be aware of that.
Walking though an half-binary-tree (that is, any element has only a pointer one following pointer) is much more flexible than a simple array of pointers. You aren't limited to a maximum number of elements and it's easy to add new elements to the beginning/end/middle, without any memmoves or possible memory leaks while reallocating memory for the grown array of pointers.
There were some problems also, like you can't get the last element with the current, but I think it won't be a real contra point.