• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
MARS_999

C++ array and polymorphic objects

30 posts in this topic

I am wondering how could you code something like this with a C style array, without making it into a vector container? I am assuming it isn't possible?

[code]
class Obj3D
{
public:
std::string id;
explicit Obj3D(const std::string& name)
{
id = name;
}
virtual ~Obj3D(){}
virtual void Draw(void) = 0;
};
class Mesh : public Obj3D
{
public:
explicit Mesh(const std::string& name) : Obj3D(name){}
virtual ~Mesh(){}
virtual void Draw(void)
{
std::cout << id << std::endl;
}
};
class Cube : public Obj3D
{
public:
explicit Cube(const std::string& name) : Obj3D(name){}
virtual ~Cube(){}
virtual void Draw(void)
{
std::cout << id << std::endl;
}
};
class Sphere : public Obj3D
{
public:
explicit Sphere(const std::string& name) : Obj3D(name){}
virtual ~Sphere(){}
virtual void Draw(void)
{
std::cout << id << std::endl;
}
};
[/code]

Now I want to make one array that holds all various derived objects....

[code]
//now place each object into array? I can't see how you can allocate these after you already allocated 1000 for Obj3D* pointers...

Obj3D* p = new Obj3D[1000];[/size][/font]// I am sure this isn't going to work....
for(int i = 0; i < 1000; ++i)
p[i] = new Mesh();//or cube, sphere... or worse yet add one at a time...

[/code]

So with a std::vector I can do this...
[code]
std::vector<Obj3D*> objects;
objects.push_back(new Mesh("BLAH"));
objects.push_back(new Sphere("BLAH"));
objects.push_back(new Cube("BLAH"));

[/code]

then run through the vector and walla...

Thanks....
0

Share this post


Link to post
Share on other sites
Why on earth would you want to do this?


Obvious problems with using raw arrays aside, you want an array of type Obj3D** - an array of pointers.
0

Share this post


Link to post
Share on other sites
Following on from what ApochPiQ said, you're pretty much asking for trouble here. Even with a vector
[code]
std::vector<Obj3D*> objects;
objects.push_back(new Mesh("BLAH"));
objects.push_back(new Sphere("BLAH"));
objects.push_back(new Cube("BLAH"));
[/code]

you still have to remember to delete the objects in the vector.

so with an array you'd have something like


[code]
Obj3D** objects = new Obj3D*[someValue];
objects[0] = new Mesh("BLAH");
objects[1] = new Sphere("BLAH");
objects[2] = new Cube("BLAH");
[/code]

but this leads to all kinds of problems. Not only do you have to delete all the objects [b]in [/b]your objects array, you have to delete the [b]array itself[/b]. and what happens when your array isn't big enough? You'll basically end up re-implementing vector (which essentially does exactly this under the hood).

My advice is to use a boost [url="http://www.boost.org/doc/libs/1_49_0/libs/ptr_container/doc/ptr_vector.html"]ptr_vector[/url]. This takes care of the clean up for you.
0

Share this post


Link to post
Share on other sites
Thanks for the replies.

So yeah a array of pointers would be the solution, but I was wondering if a single array type would work and I am getting a NO loud and clear here...

Yeah I know of the issues with deleting all the new'd memory, but I was curios to this problem as it's be awhile since I worked with this kind of code and wasn't sure what the best way to do it was...

What about C++11, is there anything in there, that's built in to solve this?

Thanks again!
0

Share this post


Link to post
Share on other sites
[quote name='MARS_999' timestamp='1334798026' post='4932654']
What about C++11, is there anything in there, that's built in to solve this?
[/quote]
I'm not sure what "solve this" is referring to (the deleting of new'd memory, or the polymorphic behavior you're wanting out of a dynamic array?). Your original question is about doing this with a C-style array, in which case the answer to the question about the automatic deleting of new'd memory is "no," because you have to manually delete the C-style dynamic array. You could create a dynamic array of smart pointers (i.e. std::shared_ptr), so you wouldn't have to manually delete each object before deleting the entire array, but you still have to delete the dynamic array yourself.

You could use a std::vector<std::shared_ptr<Obj3D>> if you don't apply the C-style array constraint, and be free of memory cleanup entirely.
0

Share this post


Link to post
Share on other sites
In your example, your derived types don't actually add any extra members, so you could ditch C++ polymorphism:[code]struct Obj3D
{
std::string id;
typedef void (FnDraw)(Obj3D*);
FnDraw* draw;
Obj3D(const std::string& id, FnDraw* draw) : id(id), draw(draw) {}
};
void DrawMesh (Obj3D* o) { std::cout << o->id << std::end; }
void DrawCube (Obj3D* o) { std::cout << o->id << std::end; }
void DrawSphere(Obj3D* o) { std::cout << o->id << std::end; }
Obj3D MakeMesh(const std::string& name) { return Obj3D(name,&DrawMesh); }
Obj3D MakeCube(const std::string& name) { return Obj3D(name,&DrawCube); }
Obj3D MakeSphere(const std::string& name) { return Obj3D(name,&DrawSphere); }

Obj3D* p = new Obj3D[1000];
for(int i = 0; i < 1000; ++i)
p[i] = MakeMesh("foo");//or cube, sphere...[/code]
0

Share this post


Link to post
Share on other sites
Hodgman, the example is really bad... I didn't think up of a great usage example when I wrote it... I was mainly looking for some info on how to deal with it, and I have become rusty... Now everyone is talking about it, I think I remember something about this from a long time ago... Thanks! :)
0

Share this post


Link to post
Share on other sites
I think this would be a more concise example....

[code]
const unsigned int SIZE = 3;
Obj3D** objs = new Obj3D*[SIZE];
objs[0] = new Mesh("BLAH");
objs[1] = new Sphere("BLAH");
objs[2] = new Cube("BLAH");
for(unsigned int i = 0; i < SIZE; ++i)
objs[i]->Draw();
for(unsigned int i = 0; i < SIZE; ++i)
delete objs[i];
delete []objs;
[/code]

But I think I am going to take a look at the various other method others have suggested...

Thanks
0

Share this post


Link to post
Share on other sites
There isn't anything wrong with using raw arrays or manual memory management at all. I would strongly suggesting thinking about whether you really need to use anything from the standard library or boost before you actually do.

The answer to you question, as previously stated is to using an array of pointers, you were only allocating an array of objects.
-1

Share this post


Link to post
Share on other sites
[quote name='Dave' timestamp='1334825747' post='4932733']
There isn't anything wrong with using raw arrays or manual memory management at all. I would strongly suggesting thinking about whether you really need to use anything from the standard library or boost before you actually do.[/quote]

I would consider this dangerous and unhelpful advice. The reverse should be true: Unless using something from the standard library or Boost is going to cause significant, documentable problems you should use those tools. Admittedly, there are situations where neither of those two will be helpful (like working on some platform with a horribly suboptimal compiler or under extreme resource constraints where you have to count each byte) but this is not the usual case.
0

Share this post


Link to post
Share on other sites
[quote]So yeah a array of pointers would be the solution, but I was wondering if a single array type would work and I am getting a NO loud and clear here...[/quote]

I'm not really sure about goal, but for code example given above, raw array or vector both behave almost identically, except that array has fixed size of 1000 elements and no count, which needs to be maintained separately.

If your question is about polymorphism, then yes, pointers to polymorphic objects can be stored in array, no problems there.

[quote]I think this would be a more concise example....[/quote]

That works, no problems here.

Either array or vector, you're storing pointers (to something). Pointers are all of equal size, determined by compiler settings and don't differ between types (with potential exception for member function pointers).


What is trickier is storing polymorphic objects by value (vector<Obj3D>, note the missing *). That is possible too, it's just much more complicated and can either improve or decrease efficiency.
0

Share this post


Link to post
Share on other sites
[quote name='BitMaster' timestamp='1334840412' post='4932784']
[quote name='Dave' timestamp='1334825747' post='4932733']
There isn't anything wrong with using raw arrays or manual memory management at all. I would strongly suggesting thinking about whether you really need to use anything from the standard library or boost before you actually do.[/quote]

I would consider this dangerous and unhelpful advice. The reverse should be true: Unless using something from the standard library or Boost is going to cause significant, documentable problems you should use those tools. Admittedly, there are situations where neither of those two will be helpful (like working on some platform with a horribly suboptimal compiler or under extreme resource constraints where you have to count each byte) but this is not the usual case.
[/quote]

If you're using STL you're usually also new'ing things up all over the place in your code. You're probably even using smart pointers now because you have no idea where things are getting cleaned up. Automatically using magic solutions like STL and Boost without any real thought (and i'm not saying this is you specifically) causes you to relax about how you are structuring and processing data.

If you organise your application well enough your memory layout will drive the architecture and you will find that you won't need STL for nearly anything.
0

Share this post


Link to post
Share on other sites
I'm still not 100% sure of the "problem", but one idea is to have an array of values for each of the different types.
[code]
const int numCubes = /* ... */;
const int numMeshes = /* ... */;
cosnt int numSpheres = /* ... */;

Cube cubes[numCubes];
Mesh meshes[numMeshes];
Sphere spheres[numSpheres];

initCubes(cubes, numCubes);
initMeshes(meshes, numMeshes);
initSpheres(spheres, numSpheres);
[/code]

One way to do this is to build a big array of pointers to these objects. The advantages here are that each group of objects is contiguous. The disadvantage here is that you have to maintain the second array, which when you're dealing with dynamic numbers of objects becomes more complicated.
[code]
Obj3D *objects[numCubes + numMeshes + numSpheres];
int index = 0;
for( int i = 0 ; i < numCubes ; ++i)
{
objects[index] = &cubes[i];
++index;
}
// Copy meshes and spheres too...

// Later
for(auto object : objects)
{
object->Draw();
}
[/code]


A second approach is to keep the arrays separate, and write generic functions for handling common routines.
[code]
template<class T>
void draw(T *objects, int count)
{
for(int i = 0 ; i < count ++i)
{
objects[i].Draw();
}
}

draw(cubes, numCubes);
draw(meshes, numMeshes);
draw(spheres, numSpheres);
[/code]
A side effect of this approach is that you are no longer making virtual calls to Draw(), the exact type is known by the compiler.

The former approach is quite convenient to write code for (once the array is correctly maintained), but uses more memory. The latter option can complicate certain algorithms that need to work across the entire data set, and results in more code generated. For example, "get the closest Obj3D" now needs to be multi-stage algorithm, instead of a simple search of one big array. Drawing transparent objects after the others, in the correct Z order, is simpler in the former case (sort the array by transparency enabled, and sub-sort the transparent portion by Z index).

Both options suffer complexity when the number of derived types get large.
0

Share this post


Link to post
Share on other sites
I wouldn't recommend hand-coding a dynamic array without an utterly compelling reason. My post was actually written a few hours ago, before the latest replies. I'd use std::vector<> as a dynamic array, writing the equivalent by hand is a good way to get additional bugs and, unless you know what you are doing, your program will actually end up slower too.
1

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1334847545' post='4932810']
I wouldn't recommend hand-coding a dynamic array without an utterly compelling reason. My post was actually written a few hours ago, before the latest replies. I'd use std::vector<> as a dynamic array, writing the equivalent by hand is a good way to get additional bugs and, unless you know what you are doing, your program will actually end up slower too.
[/quote]

You shouldn't really need dynamic arrays, is my point. Not if you've thought things through.
0

Share this post


Link to post
Share on other sites
It's nice to know you deny the existence of C++ programs that work with text. It helps put your bad advice into context.
0

Share this post


Link to post
Share on other sites
[quote name='Dave' timestamp='1334848546' post='4932817']
[quote name='rip-off' timestamp='1334847545' post='4932810']
I wouldn't recommend hand-coding a dynamic array without an utterly compelling reason. My post was actually written a few hours ago, before the latest replies. I'd use std::vector<> as a dynamic array, writing the equivalent by hand is a good way to get additional bugs and, unless you know what you are doing, your program will actually end up slower too.
[/quote]

You shouldn't really need dynamic arrays, is my point. Not if you've thought things through.
[/quote]
[code]
struct Pixel
{
unsigned char r;
unsigned char g;
unsigned char b;
};

struct Image
{
int width;
int height;
// Oh shiz, what now?! Obviously I haven't thought things through if I want to have images with varying sizes.
// I was going to create an std::vector<Pixel> (or Pixel* if I was using C), but I guess I just need to "think things
// through" more so I don't have to use dynamic arrays, because obviously I'm doing it wrong if I use them! I
// guess the better option would be to create a Pixel[100000]. Yes, that sounds like a better solution. That way
// I can only support images with 100000 pixels, and even if I have an 8x8 pixel image, I really think all 100000
// pixels are necessary. Yes, this is better.
};

[/code]
1

Share this post


Link to post
Share on other sites
[quote name='SiCrane' timestamp='1334848746' post='4932820']
It's nice to know you deny the existence of C++ programs that work with text. It helps put your bad advice into context.
[/quote]

Or you could just give yourself some good old max sizes.
-3

Share this post


Link to post
Share on other sites
[quote name='Dave' timestamp='1334844886' post='4932795']...[/quote]I appreciate this advice, but I'd probably still advise people first learn to use STL/RAII/etc ([i]before[/i] [i]going back to[/i] [i]keeping it simple) [/i][i][img]http://public.gamedev.net//public/style_emoticons/default/wink.png[/img][/i]

Personally, I learned C++ first in a "[i]c with classes[/i]" way, then pure C++ with STL/boost/metaprogramming, then back to something more like "[i]c++ with structs[/i]".

e.g.
My current game only has 1 [font=courier new,courier,monospace]malloc[/font] ([i]and some [font=courier new,courier,monospace]new[/font]'s in middleware bindings[/i]) that places a large address range into a [url="http://publications.dice.se/attachments/scopestacks_public.pdf"][font=courier new,courier,monospace]scopestack[/font][/url], in which you can allocate objects or other [font=courier new,courier,monospace]scopes[/font]. It's still proper C++ with reliable constructors/destructors/etc and RAII, but instead of the "smart pointer" type only maintaining the lifetime of 1 link, it is instead a [i]container[/i] of links ([i]or actually, a linked-list of destructors to call when the scope is unwound[/i]). When using POD types, allocation and deallocation are just pushing/popping from a stack of bytes ([i]almost free, like regular stack/local allocation[/i]), but with a 'scope' object representing their lifetime.

This makes "dynamic memory allocation" as easy and predictable ([i]bug-friendly[/i]) as regular C/C++ stack allocation rules, without ever worrying about using the heap ([font=courier new,courier,monospace]new[/font]/[font=courier new,courier,monospace]malloc[/font]). [b]It also means that you can use raw arrays and pointers much more safely and easily[/b] than with C++ smart pointers. Raw "unsafe" code can really be easier and simpler [i]in the right conditions -- IM[/i], the right way to learn about those conditions is to first learn the standard (STL) way to manage things, and then see what junk you can strip out once you grok it.

To illustrate, if a Foo class had to maintain a Bar member using dynamic allocation:
In C with classes:[code]class Foo // verbose, explicit
{
public:
Bar* m;
void Init() { m = (Bar*)malloc(sizeof(Bar)); Bar::Init(m); }
void Deinit() { free(m); }//oh damn this verbosity, I forgot to call Bar::Deinit(m)
};
Foo* test = (Foo*)malloc(sizeof(Foo));
Foo::Init(m);
Foo::Deinit(m);
free(test);[/code]In basic C++:[code]class Foo : NonCopyable //copy constructor/assignment operator need to be implemented if copyable (rule of three)
{
Bar* m;
public:
Foo() m(new Bar) {}
~Foo() { delete m; }
};
Foo* test = new Foo;
delete test;[/code]In modern C++:[code]class Foo
{//N.B. actually smart_pointer, auto_ptr, etc
smart_pointer<Bar> m;// handles destructor and copy (via reference counting)
public:
Foo() m(new Bar) {}
};
smart_pointer<Foo> test( new Foo );[/code]With a simple stack allocator:[code]class Foo
{
Bar* m;
public:
Foo(Scope& a) m(myNew(a,Bar)) {}//member should have same scope as parent, no need for destructor or copy
// copies are weak (possibly dangling, not ref-counted) pointers just like the basic C++ / C examples, so should only be passed to objects *closer to the top of the stack*.
};
Stack stack( malloc(GB(1)), GB(1) );
Scope a( stack );
Foo* test = myNew(a, Foo)(a);//increments a stack alloc, constructs, adds destructor to scope

//@ Cornstalks -- struct Image // Oh shiz, what now?!
struct Image
{
int width, height;
int size() {return sizeof(Image) + sizeof(pixel)*width*height;}
pixel* begin() { return (pixel*)(this+1); }
pixel* end() { return begin() + width*height; }
}
Image* test = myAlloc(a, Image::size());//increments a stack alloc, valid until 'a' is destructed[/code]
0

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1334849296' post='4932827']
[quote name='Dave' timestamp='1334848546' post='4932817']
[quote name='rip-off' timestamp='1334847545' post='4932810']
I wouldn't recommend hand-coding a dynamic array without an utterly compelling reason. My post was actually written a few hours ago, before the latest replies. I'd use std::vector<> as a dynamic array, writing the equivalent by hand is a good way to get additional bugs and, unless you know what you are doing, your program will actually end up slower too.
[/quote]

You shouldn't really need dynamic arrays, is my point. Not if you've thought things through.
[/quote]
[code]
struct Pixel
{
unsigned char r;
unsigned char g;
unsigned char b;
};

struct Image
{
int width;
int height;
// Oh shiz, what now?! Obviously I haven't thought things through if I want to have images with varying sizes.
// I was going to create an std::vector<Pixel> (or Pixel* if I was using C), but I guess I just need to "think things
// through" more so I don't have to use dynamic arrays, because obviously I'm doing it wrong if I use them! I
// guess the better option would be to create a Pixel[100000]. Yes, that sounds like a better solution. That way
// I can only support images with 100000 pixels, and even if I have an 8x8 pixel image, I really think all 100000
// pixels are necessary. Yes, this is better.
};

[/code]
[/quote]

This is not a valid point.

This topic has been discussing the use of std::vector of c-style arrays, for your image scenario you don't need a std::vector anyways so i'm not sure what your point is. Dynamic allocation is not the discussion here.
-1

Share this post


Link to post
Share on other sites
[quote name='Dave' timestamp='1334849596' post='4932831']
[quote name='SiCrane' timestamp='1334848746' post='4932820']
It's nice to know you deny the existence of C++ programs that work with text. It helps put your bad advice into context.
[/quote]

Or you could just give yourself some good old max sizes.
[/quote]
You realize that's the attitude behind 95% of the security errors in C++ programs, right?
0

Share this post


Link to post
Share on other sites
[quote name='SiCrane' timestamp='1334850401' post='4932839']You realize that's the attitude behind 95% of the security errors in C++ programs, right?[/quote]To take this in another direction, what if your only 'max size' was your main/large memory allocations?
Max-sizes for buffers can still be set [i]dynamically[/i] ([i]which is probably the fix to those security bugs -- e.g. allocating the [u]correct max size[/u] for a string before [font=courier new,courier,monospace]strcpy[/font]ing into it[/i]) instead of hard-coded, as long as they fit in your RAM max size.
0

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1334851123' post='4932843']
[quote name='SiCrane' timestamp='1334850401' post='4932839']You realize that's the attitude behind 95% of the security errors in C++ programs, right?[/quote]To take this in another direction, what if your only 'max size' was your main/large memory allocations?
Max-sizes for buffers can still be set [i]dynamically[/i] ([i]which is probably the fix to those security bugs -- e.g. allocating the [u]correct max size[/u] for a string before [font=courier new,courier,monospace]strcpy[/font]ing into it[/i]) instead of hard-coded, as long as they fit in your RAM max size.
[/quote]
If your max size is set dynamically, you're still using dynamic arrays, and since Dave agreed with you, I see that he's conceded the point about not being able to get rid of dynamic arrays when dealing with text.
0

Share this post


Link to post
Share on other sites
My "Yep" was agreeing with your previous post actually SiCrane, but he replied quicker than i did and i can't seem to delete it... Can you do that on these forums?
0

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this  
Followers 0