World object design doubt (memory allocation)
Hello everyone!
I'm working on my first demo, a 3D third person shooter in OpenGL.
I've a code design doubt concerning the world objects allocation and deallocation.
First of all I have designed the game world with those three classes:
1) ObjectAbs: abstract class that represents a generic object in the world.
2) Room: The world is composed by rooms, the Room class has an array of pointers to ObjectAbs.
3) World: it is a static class that contains an array of pointers to Room
Inside main.cpp, during the initialization, the static method "World::init();" is called,
that static method contains a monolithic creation of all the world objects, rooms etc..
(I know, I should create the world by reading a txt file... I will soon do that)
Anyway inside that monolithic static method I'm creating all the Objects by calling the new operator.
for instance:
//Number of Obj allocated to Room1
int room1StaticObjsNum = 10;
//Room1's Objs array allocation
ObjectAbs **room1Objs = new ObjectAbs*[zone1StaticObjsNum];
//Wall created and added to the room1 array (Wall extends ObjectAbs)
zone1StaticObjs[0] = new Wall( ... );
//Room1 created and added to the World's Room array
World::_zones[0] = new Zone( ... room1Objs ...);
I'm using the new operator because the objects won't be destroyed when the method ends, and since there is no need to free memory during the game (because actually it is only one big level) I thought there was no need to formally free the memory but leave that to windows when the application is closed.
I'm feeling that creating and (not)managing objects in that way is not really right.
I think that the problem is mainly caused by the fact that World is a static class.
Before modifying again the design I wanted to know if someone could suggest me a good way to organize the world objects and the responsibilities of deleting them.
Here it is the extract of the three class interfaces:
-----------------------------------------------------
#ifndef OBJECT_ABSTRACT_H
#define OBJECT_ABSTRACT_H
class ObjectAbs
{
public:
ObjectAbs( ... ): ... {}
virtual ~ObjectAbs() {}
virtual void draw() const = 0 ;
protected:
float _xPos;
float _yPos;
float _zPos;
...
};
#endif //OBJECT_ABSTRACT_H
-----------------------------------------------------
#ifndef ROOM_H
#define ROOM_H
#include "Objects\ObjectAbs.h"
class Room
{
public:
Room( ... ObjectAbs **objs ... );
void draw() const;
void update();
bool _active;
private:
const int _objNum;
ObjectAbs **_objs;
};
#endif //ROOM_H
-----------------------------------------------------
#ifndef WORLD_H
#define WORLD_H
#include "room.h"
class World
{
public:
static void init();
static void draw();
static void update();
static Room *_currentRoom;
private:
World();
static int const RoomNumber = 4;
static Zone *_rooms[];
};
#endif //WORLD_H
-----------------------------------------------------
That's all, I hope someone will answer, thanks! :D
Probably the best place to start your research is to look into so-called "smart pointer" classes. These replace raw pointers (which are dangerous in general) and, depending on the type of smart pointer, will provide various semantics for automatically cleaning up your memory when it is no longer in use.
I'd go back to basics and (like you mentioned you will) look at why World is a static class - there's no reason for it to be. Then you have the concept of what "owns" the resource.
A World owns the Rooms, so it's correct to new(), and because it's the owner it's totally correct to delete() them when finished (probably from the class Destructor). The rooms don't exist without the world. Don't forget to implement a derived Room destructor.
If I were you I'd probably be looking to avoid dynamic allocation altogether - why not have an array of Rooms[MAX_ROOMS]? Something like that is not going to take up much memory.
Personally I try and avoid smartPtrs whenever possible, as it hides the complexity of what is happening in the background. When you get to the situation where you need an object which has an undefined lifespan, then start looking at reference counted classes such as smart pointers - such as for shared texture resource.
Remember - KISS - keep it simple stupid ;)
A World owns the Rooms, so it's correct to new(), and because it's the owner it's totally correct to delete() them when finished (probably from the class Destructor). The rooms don't exist without the world. Don't forget to implement a derived Room destructor.
If I were you I'd probably be looking to avoid dynamic allocation altogether - why not have an array of Rooms[MAX_ROOMS]? Something like that is not going to take up much memory.
Personally I try and avoid smartPtrs whenever possible, as it hides the complexity of what is happening in the background. When you get to the situation where you need an object which has an undefined lifespan, then start looking at reference counted classes such as smart pointers - such as for shared texture resource.
Remember - KISS - keep it simple stupid ;)
Quote:Original post by Noggs
Personally I try and avoid smartPtrs whenever possible, as it hides the complexity of what is happening in the background.
No offense but that is the most ridiculous advice I have ever heard, the goal of good software design is to hide complexities not bring it to fore, it's called abstraction.
You should try to avoid complexity whenever you can, unless you are some sort of programming masochist; just like you said, follow the KISS approach, and the main place to maintain the simplicity is at the interface, that is what smart pointers aim to do.
If you really want to play about with, and learn about raw memory manipulation then learn some assembly and whip out an assembler, and leave it firmly as a learning exercise unless you wish to do embedded or OS kernel development.
If on the other hand you wish to develop complex software then use modern software development techniques, that involves sufficient abstract and following the DRY approach.
My point is why use a smart pointer when a raw pointer is perfectly acceptable in this instance? Always use the simplest approach. Pointers, arrays, references - these things are core features of the language - use them first! If you need to create an object and you don't know when it will be able to be released - use a smart pointer!
Quote:Original post by Noggs
Pointers, arrays, references - these things are core features of the language - use them first!
As of C++0x, std::shared_ptr is also a core part of the language [wink]
You're correct in saying that if you can use a regular stack instance instead of a dynamically allocated one you should, but if you're going to use a raw pointer you might want to think about a shared_ptr instead. It doesn't mean you should never use raw pointers or that a shared pointer is always appropriate, just that once you cross that boundary you should consider all alternatives (especially now that they're an official part of the language).
Quote:My point is why use a smart pointer when a raw pointer is perfectly acceptable in this instance? Always use the simplest approach. Pointers, arrays, references - these things are core features of the language - use them first! If you need to create an object and you don't know when it will be able to be released - use a smart pointer!As noted, smart pointers are now part of the core language (having been incorporated into the standard library), so your argument fails there. (And if by 'core features' you're exluding the standard library, then you're essentially saying that you should prefer raw character arrays over std::string, which isn't terribly good advice.)
Regardless though, using a smart pointer to manage the lifetime of an object and avoid manual deallocation makes the code simpler, not more complex; it's analogous to using std::vector rather than a raw dynamically allocated array (which most would advocate).
Quote:Original post by Noggs
My point is why use a smart pointer when a raw pointer is perfectly acceptable in this instance? Always use the simplest approach. Pointers, arrays, references - these things are core features of the language - use them first! If you need to create an object and you don't know when it will be able to be released - use a smart pointer!
I've found that sticking to raw pointers where possible can force me to simplify my design a lot, and that can be extremely useful.
I think it also forces you to understand the lifetime of your objects, which is also a good thing. That's not to say you should spurn perfectly good tools - just know when it makes more sense to use scoped_ptr rather than blindly using shared_ptr everywhere, for example.
Quote:
(And if by 'core features' you're exluding the standard library, then you're essentially saying that you should prefer raw character arrays over std::string, which isn't terribly good advice.)
I guessed when I was writing the comment about smart pointers on this forum that I was inviting some flaming [grin]. I concede that the use of shared_ptr is not so bad, however when it comes to stl that's a different matter, although it does depend on context. If working on a platform where memory is tight and per frame dynamic allocations are generally frowned upon, the use of stl can be crippling - particularly the std::string class - and very easy to abuse.
The other problem I have with it is that if you are just starting out the compile errors generated by using stl incorrectly can be pretty confusing.
Please understand I'm not advocating the approach of write everything yourself, by all means use shared_ptrs, vectors, strings. I'm just saying that it should not necessarily be the first port of call to solve a problem. Being forced to think carefully about exactly what you want to accomplish, what data is needed where and when, will result in faster and simpler algorithms.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement