Sign in to follow this  

c++'s "new" clarification

This topic is 4201 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

i really need a refresher on what the "new" operator does and most importantly does not do. the reason i want to know is because when trying to create a .3ds loader fuction i wanted to use dynamic arrays to store the vertex coordinates, polygon vertex numbers, and mapping coordinates. the reason i thought this would work is because the .3ds file format stores the count of each one of those properties. however, once i had my function written i noticed that when i wanted to display all those triangles the program would crash (before every displaying a single frame so i narrowed it down to the display function). so with some fiddling i found out that i could only display at max 176 polys from the dynamic array. this bothered me to say the least so i went back to the tried and true method of over allocating space with constants. but then i'm back to where i didn't want to be -- creating a class with over allocated space. so my question is this -- is there a way to allocate the space dynamically other than the new operator? if there is, is there a way to make sure it is contiguous for proper use as an array? and if there isn't a way other than the "new" operator, how can i make sure that the array is going to get the space it needs? or more imporantly, should i even bother with the dynamic implementation and just stick with static declerations? and for reference (if my explanation at 4:30am isn't too clear :) ) here is an example code snipet:
//original structs
typedef struct
{
  float x, y, z;
}vertex_type;

typedef struct
{
  int a, b, c;
}polygon_type;

typedef struct
{
  float u, v;
}mapcoord_type;

typedef struct
{
  vertex_type* vertex;
  polygon_type* polygon;
  mapcoord_type* mapcoords;
  int numvertices;
  int numpolygons;
}obj_type;


//the current obj_type struct which is the only one to change
typedef struct
{
  vertex_type vertex[20000];
  polygon_type polygon[15000];
  mapcoord_type mapcoords[20000];
  int numvertices;
  int numpolygons;
}obj_type;


// an example of the original loading code (this is for loading polygon data,
// the others are very similar with only small changes, mostly referenced from 
// spacesimulator.net)
case 0x4120:
  fread (&l_quantity, sizeof (unsigned short), 1, l_file);
  testobj.numpolygons = l_quantity;
  testobj.polygon = new polygon_type[l_quantity]; // <- this is now commented out 
  for (i=0; i<l_quantity; i++) // since there is no need for the "new" operator with 
  {                            // the predetermined array size
    fread (&testobj.polygon[i].a, sizeof (unsigned short), 1, l_file);
    fread (&testobj.polygon[i].b, sizeof (unsigned short), 1, l_file);
    fread (&testobj.polygon[i].c, sizeof (unsigned short), 1, l_file);
    fread (&l_face_flags, sizeof (unsigned short), 1, l_file);
  }
break;

Share this post


Link to post
Share on other sites
That all seems fine to me. new and new[] will allocate a contiguous block of memory large enough to hold the object or array of objects requested, and will then call the constructor or constructors of all elements in the array.
The only other portable way to allocate memory is malloc(), but that's C and won't call constructors. There are other platform specific routines too, but that's overkill for this.

You should stick with new[], there must be a problem elsewhere in your code.

Share this post


Link to post
Share on other sites
Quote:
Original post by supercat1
so my question is this -- is there a way to allocate the space dynamically other than the new operator?


Why don't you want to use new ?

int* ptr = new int[42]; // allocates memory for 42 ints

Quote:
or more imporantly, should i even bother with the dynamic implementation and just stick with static declerations?


Large arrays on the stack == just don't do it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
Quote:
or more imporantly, should i even bother with the dynamic implementation and just stick with static declerations?


Large arrays on the stack == just don't do it.


Would you mind explaining why?

Share this post


Link to post
Share on other sites
precisely! i don't want that overhead because it is just so wastefull.

but about the issue in the code -- i don't believe that it is in the loading function at all. i ruled that out fairly early because when i printed all the vertex and polygon data to the screen it worked perfectly fine and i had all 13k+ polys, 11k+ verts, and i didn't bother to check the mapcoords because there were no textures yet. however, there was one consistant issue -- the first two values of the polygon input (for polygon[0].a and polygon[0].b) were huge values that weren't indexes for the vertex array. this is what caused my program to crash i found out, so the fix for that was to make the display loop i=1 instead of i=0 (the side effect being that one poly wasn't there). however then i discovered the size limitation. and i have no idea what caused that.

i'm gonna go test this again see if maybe it will work now.

...
...
...

my computer just made a liar out of me. sometimes i really don't know why some errors show up at times and not others. i'll blame the early hour and dev-cpp and not myself. :) in other words, it all works now! tomorrow's goal -- put in lights and try not to make a post and look stupid. :D

thanks for afirming what i thought about the new operator and i really don't know what i did to change anything.

here is the new poly reading function though:

case 0x4110: // this reads the vertex data, it works just fine as far as i can tell
fread(&l_quantity, sizeof(unsigned short), 1, l_file);
testobj.numvertices = l_quantity;
testobj.vertex = new vertex_type[l_quantity];

for(i = 0; i < l_quantity; i++)
{
fread(&testobj.vertex[i].x, sizeof(float), 1, l_file);
fread(&testobj.vertex[i].y, sizeof(float), 1, l_file);
fread(&testobj.vertex[i].z, sizeof(float), 1, l_file);
}
break;

case 0x4120:
fread (&l_quantity, sizeof (unsigned short), 1, l_file);
testobj.numpolygons = l_quantity;
testobj.polygon = new polygon_type[l_quantity];
for (i=0; i<l_quantity; i++)
{
fread (&spaceeater, sizeof (unsigned short), 1, l_file);
testobj.polygon[i].a = spaceeater;
fread (&spaceeater, sizeof (unsigned short), 1, l_file);
testobj.polygon[i].b = spaceeater;
fread (&spaceeater, sizeof (unsigned short), 1, l_file);
testobj.polygon[i].c = spaceeater;
// these are the original assignment statements
// fread (&testobj.polygon[i].a, sizeof (unsigned short), 1, l_file);
// fread (&testobj.polygon[i].b, sizeof (unsigned short), 1, l_file);
// fread (&testobj.polygon[i].c, sizeof (unsigned short), 1, l_file);
fread (&l_face_flags, sizeof (unsigned short), 1, l_file);
}
break;


Share this post


Link to post
Share on other sites
Quote:
Original post by Ezbez
Quote:
Original post by Fruny
Quote:
or more imporantly, should i even bother with the dynamic implementation and just stick with static declerations?


Large arrays on the stack == just don't do it.


Would you mind explaining why?


Because there is a limit to the size of the stack for a program.


int main()
{
int m[300000];

printf("Hello there");

return 0;
}

When I try to run this program, the program crashes. But when I run it with "int* m = new int[300000];" instead, it runs just fine.

Even if you don't allocate enough at a single time to cause a crash, since the stack is used for keeping track of all the local variables and return addresses (the address of the function to return to after a function has finished running), if you allocate a lot on the stack, you might be fine for a while, but after that allocation, as the call stack gets deeper and deeper, you could crash simply by entering a function, even if the function is fairly trivial (like a single 'printf' call).

The max I've ever allocated on the stack was a a kilobyte, and that was for a string I was using to read in from a text file. Basically if you need something larger than that, you should really think about dynamic allocation.

Share this post


Link to post
Share on other sites
Your code practically pure C code, not C++. C++ gives us a better set of tools to work with. In particular it gives us the class std::vector, which is a dynamic array class which handles memory management and keeps track of its size. Its much less error-prone than manually handling memory management and length tracking yourself.

To use a std::vector you must include the header <vector>. std::vector is a template class, so to declare a vector of X you say std::vector< X >. A vector of vertex_type is therefore std::vector< vertex_type >. vector supports array indexing just like raw arrays and the memory is guaranteed to be contiguous, so you can take a pointer to the first element (providing the vector is not empty) and treat the result just like a pointer to a raw array. To get the number of elements stored in a vector you can use the size() member function.

So, using std::vector, and with a few other changes, you code would become:
// the typedef struct idiom is unneccessary in C++.
struct vertex_type
{
float x, y, z;
};

struct polygon_type
{
// name your variables expressively
int vertex_1_index, vertex_2_index, vertex_3_index;
};

// C++ uses streams for I/O. One advantage of stream objects is that you
// can provide overloads of the stream operators to load and save objects
// of user defined types
std::istream & operator>>(std::istream & stream, polygon_type & polygon)
{
// you can't just read two bytes into a four byte type and expect things
// to work, so read into a two byte type and assign into the four byte
// type to get guaranteed correct behaviour.
unsigned short value;
stream.read(reinterpret_cast< char * >(&value), sizeof (unsigned short));
testobj.polygon[i].vertex_1_index = value;
stream.read(reinterpret_cast< char * >(&value), sizeof (unsigned short));
testobj.polygon[i].vertex_2_index = value;
stream.read(reinterpret_cast< char * >(&value), sizeof (unsigned short));
testobj.polygon[i].vertex_3_index = value;
}

struct mapcoord_type;
{
float u, v;
};

struct obj_type
{
// vectors replace pointers to raw arrays
std::vector< vertex_type > vertices;
std::vector< polygon_type > polygons;
std::vector< mapcoord_type > mapcoords;
};

// an example of the original loading code (this is for loading polygon data,
// the others are very similar with only small changes, mostly referenced from
// spacesimulator.net)
case 0x4120:

// C++ uses streams for I/O. One advantage of stream objects is that they
// automatically close themselves when they go out of scope
file_reader.read(reinterpret_cast< char * >(&l_quantity), sizeof (unsigned short));

// an efficiency optimisation - we know how many polygons there are going to
// be so we can preallocate sufficient memory to hold them all and avoid
// additional reallocations
testobj.polygons.reserve(l_quantity);

for (i = 0; i < l_quantity; ++i)
{
polygon_type polygon;

// read a polygon from the file
file_reader >> polygon;

// append the polygon onto the end of the vector
testobj.polygons.push_back(polygon);

stream.read(reinterpret_cast< char * >(&l_face_flags), sizeof (unsigned short));
}
break;

Σnigma

Share this post


Link to post
Share on other sites
ah, now i had considered using the vector class however, i believe what it does is that whenever it needs to resize it actually will double its size by creating a new array and copying everything to that. i personally am not a fan of the vector (or many of the stl) classes because i can't be sure it is behaving how i want it to behave.

also, your "reserve" call is no more efficient than calling the "new" operator. in fact, i believe that it is less space efficient because you have the vector class over head.

i agree, many of c++'s stream operating tricks are nice (and i use them in my school projects all the time), however, i'm not in the state of mind to track down any errors when writing them. :) also, like i said in my comments, much of this code is derived from spacesimulator.net because it has the best tutorial on how to load data from a .3ds file. the one draw back to that tutorial is it only deals with loading vertices, polygon vertex indices, and u/v coordinates when in fact the .3ds file will also tell you the texture name and many other wonderful things. :)

so, yeah, i know it isn't great c++ in that area, but the rest of my code is pretty much true c++. plus, i'm developing on dev-c++ and it seems to behave the best with these functions.

having said that, if i were to go back and redo this whole thing i would probably do exactly what you had written -- true c++ style (except without the vector class for the reasons i stated above).

Share this post


Link to post
Share on other sites
Quote:
Original post by supercat1
ah, now i had considered using the vector class however, i believe what it does is that whenever it needs to resize it actually will double its size by creating a new array and copying everything to that. i personally am not a fan of the vector (or many of the stl) classes because i can't be sure it is behaving how i want it to behave.


Not being sure of how the STL behaves isn't a good reason to avoid it. However, it is a good reason to learn how the STL actually does works. You can a learn great deal about algorithm and data structure design by reading the source of an STL implementation.

Quote:
Original post by supercat1
also, your "reserve" call is no more efficient than calling the "new" operator. in fact, i believe that it is less space efficient because you have the vector class over head.


More efficient - no, but it is safer, easier, and less prone to memory leaks than the code you write yourself.

The overhead for most std::vector implementations is 12 bytes per vector. When you are dealing with ten thousand triangles - I hardly think it matters eh?

Share this post


Link to post
Share on other sites
In modern OS, it is the OS who allocate the memory for the applications running. So, if you really don't want to use the new syntax in C++, you can call up the OS specific API. For example in Win32, you can call HeapAlloc(), GlobalAlloc().

Though, with the above methods, you need to help managing the memory with the OS and there is a lack of flexability in the OS to optimize the physical and virtual memory.

For C++ specific construct like inheritance and virtual function, the new syntax will help calling the appropriate auto generated codes to prepare the vtable.

Share this post


Link to post
Share on other sites
Quote:
Not being sure of how the STL behaves isn't a good reason to avoid it. However, it is a good reason to learn how the STL actually does works. You can a learn great deal about algorithm and data structure design by reading the source of an STL implementation.


or i could just write my own classes that do only what i want/need to do instead of reading through an stl implementation. :) that is what i generally do, so then i have my own set of data types that i can reuse over and over and mod however i feel is necessary.

but, that wasn't the original intent of the post, which was just a refresher on what the "new" operator actually does. however, the original issue i encountered has been solved. :) thanks for all the input!

Share this post


Link to post
Share on other sites
Quote:
Original post by supercat1
ah, now i had considered using the vector class however, i believe what it does is that whenever it needs to resize it actually will double its size by creating a new array and copying everything to that. i personally am not a fan of the vector (or many of the stl) classes because i can't be sure it is behaving how i want it to behave.

std::vector provides guaranteed amortized constant time insert and erase operations at the end of the container. This is achieved my increasing the size of the container exponentially whenever it needs to grow. You should be able to rely on your compiler vendor to tune their vector implementation to the platform so that platforms with more memory (like windows) will grow more aggresively than platforms with less memory. In addition resizes which exceed exponential growth will usually be exact or rounded up (maybe to minimise memory fragmentation/wastage). vector is really a very clever class implemented by absolute experts who probably know the compiler and the platform better than you know the back of your hand.

Quote:
also, your "reserve" call is no more efficient than calling the "new" operator. in fact, i believe that it is less space efficient because you have the vector class over head.

No, the reserve call is not more time-efficient than a call to new. It's an optimisation in the sense that it is (slightly) more efficient then repeatedly calling push_back without a call to reserve. In your particular code some of vectors advantages are lost but the overhead is going to be literally insignificant.

Quote:
i agree, many of c++'s stream operating tricks are nice (and i use them in my school projects all the time), however, i'm not in the state of mind to track down any errors when writing them. :)

Huh? They are never more error-prone than fread and often less so. How can you be in the state of mind to track down errors with FILE *s and not fstreams?

Quote:
<snip />
so, yeah, i know it isn't great c++ in that area, but the rest of my code is pretty much true c++. plus, i'm developing on dev-c++ and it seems to behave the best with these functions.

Define "behave the best".

Quote:
having said that, if i were to go back and redo this whole thing i would probably do exactly what you had written -- true c++ style (except without the vector class for the reasons i stated above).

Well, if you're not going to use vector you should at least use a smart pointer instead. Unless you really want to take responsibility for exception safety and copy/assignment semantics. Plus you'll be losing the ability to plug in a debug version of the SC++L and get all your vector accesses range checked.

Quote:
or i could just write my own classes that do only what i want/need to do instead of reading through an stl implementation. :) that is what i generally do, so then i have my own set of data types that i can reuse over and over and mod however i feel is necessary.

So you'd rather implement your own versions which will almost certainly be less efficient and more error prone? I used to like writing my own container classes because I thought it made me a better programmer. Then I got wise and realised it actually made me a worse programmer. Shortly after that I realised just how flawed my implementations were. Learn the SC++L and use it. You'll be thankful in the long term.

Σnigma

Share this post


Link to post
Share on other sites
anyways, again, this really isn't the original topic and so i'm not too interested in arguing with you about. thanks for your input, but i'll do it how i feel most comfortable. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by supercat1
or i could just write my own classes that do only what i want/need to do instead of reading through an stl implementation. :) that is what i generally do, so then i have my own set of data types that i can reuse over and over and mod however i feel is necessary.


That's a commendable attitude and one that many of us started with, but you'll soon realize that if you want to get work done that in the long run its much easier on you if you learn stl rather than rewriting it each time.

Hope I helped sway you:)

Cheers
Chris

Share this post


Link to post
Share on other sites

This topic is 4201 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this