C++ SDL - Deleting dynamically allocated objects

Started by
15 comments, last by Servant of the Lord 10 years, 2 months ago

I may have missed something, but is there a reason to keep the Background object as a pointer?


Then you don't have much control over memory. I could call the Unload function to drop a texture but the object and its data will still be there till
out of scope or game end, which is pretty much a waste. Why remain the object till game loop ends when it is no useless anymore (sprite without texture in this case) and you could drop it from memory.

std:make_unique is awesome, although i use MinGW not Visual Studio's compiler.
I guess i will stick with


int* a = new int;
delete a;
a = nullptr;
delete a; // <-- OK

if its totally safe.


bool useless () const {return isUseless;}

static void clean_up () {
for (/*every object in MasterVector*/)
if (object->useless()) {
vec.erase(/*object*/);
delete /*object*/;
}
}

static void clean_up (std::vector vec*) {
for (/*every object in vec*/)
if ( object->useless() ) {
vec->erase(/*object*/);
delete /*object*/;
}
}

void update () {
/*do anything it needs to do*/
if (/*something very very sad happened*/)
isUseless = true;
}


dejaime im i missing a point or you i dont know. I dont understand your solution pretty much.
You keep objects in dynamic array and check for their state if they are not useless then you delete them.
This isUseless is only checked in clean_up() function
As far as i understood you remove the object from the array when it is useless but you are not deleting it till cleanup, which pretty much can be called within game loop or end game. And again if you call clean_up() in game loop you need to make sure to call it in the end game if object is not useless, which again leads back to my problem.
But if you only assign isUseless in the game logic and do clean_up() in the end it basically means that the object with its data except the texture will still remain in memory till endgame. I want to free things from memory since they are not usable anymore. Probably i miss something i don't know and your solution may be efficient enough, but it does not seem to be structured well as you know that in game you don't deal with specific objects only. So keeping structured attaching same logic for everything helps readability and not getting frustrated when things go complex.

Advertisement

MinGW 4.7 onward (if i remember the version correctly) supports most of C++11, if not all of it, and unique_ptr is part of that.

His solution avoids deleting stuff in the middle of the game loop. If you do a lot of deleting and allocating within the main loop, you're going to run into hiccups in your framerate when you kill a lot of stuff at the same time. Also, iterating over a container of objects and deleting them at the same time is a bad idea and just invites bugs and/or crashes.

devstropo.blogspot.com - Random stuff about my gamedev hobby

MinGW 4.7 onward (if i remember the version correctly) supports most of C++11, if not all of it, and unique_ptr is part of that.


Yes, however std::make_unique requires C++14. If and how much C++14 is supported will depend on what version of MinGW is used. Of course it can't hurt to specify -std=c++14 instead of -std=c++11 and just try it out.

Then you don't have much control over memory. I could call the Unload function to drop a texture but the object and its data will still be there till
out of scope or game end, which is pretty much a waste.


That's a terrible reason. We're talking about some bytes on the stack. Your software _design_ should trump micro-optimization of a few bytes. Yes, that applies to games, even really big and complex multi-million-dollar-budget ones.

MinGW 4.7 onward (if i remember the version correctly) supports most of C++11, if not all of it, and unique_ptr is part of that.


Yes, however std::make_unique requires C++14. If and how much C++14 is supported will depend on what version of MinGW is used. Of course it can't hurt to specify -std=c++14 instead of -std=c++11 and just try it out.


It's in libstdc++ 4.8 at least, which I believe is in the latest MingW. You don't _need_ make_unique if you aren't using exceptions (the purpose of make_unique is to add sequencing), and if you do decide you need it but currently don't have it, it is trivial to write your own copy:

template <typename T, typename ...P>
auto make_unique(P&&... argv) -> std::unique_ptr<T>
{
   return std::unique_ptr(new T(std::forward<P>(argv)...));
}
I personally use an alias 'my' for unique_ptr (actually a custom reimplementation of unique_ptr for reasons irrelevant to this discussion) and a custom 'box' version of make_unique since I use them so often (far more often than raw pointers or new/delete operator calls, respectively) and firmly believe they should have short and easy-to-type names that encourage you to use them more even when you're in a crunch for deadline (when most devs throw out many notions of good practice to get things done in time, in my experience). I'm not 100% certain that using these alternate non-standard names is the best idea long-term as I've had relatively few other developers give me any real-world feedback on this particular issue (but what I've gotten has been positive, at least), but it's something you might consider.

Sean Middleditch – Game Systems Engineer – Join my team!

I guess i will stick with


int* a = new int;
delete a;
a = nullptr;
delete a; // <-- OK
if its totally safe.


int* a = new int;
int* b = a;
delete a;
a = nullptr;
delete b; // <-- ooh, ouch, this is a more likely scenario

Stephen M. Webb
Professional Free Software Developer

What i like to do is have a class be the manager of actors (an entity in the game).

This class is in charge of everything related to actors like maintaining a list of them, checking if they are alive, updating them and even drawing them (well, the calss check if it need to be drawn, then call another class to draw). I keep a std::vector of all actors.

When an actor is killed, i do not delete it yet, i only set the flag for it to be deleted on the next frame.

In my game loop is a function i call "MaintainActiveList". What this do is loop throught all actors and check if they are alive and if they are on screen.

If the dead flag is set, i delete them and remove them from the array.

When the program quit (or change map etx...) i loop through all remaining actors and delete them.

The advantage this did to my game is actors are only updated and drawn if they are active and alive.


void CActorManager::MaintainActiveList()
{
	mActiveActors.clear();
	for (std::vector<IActor*>::iterator i = mActors.begin(); i != mActors.end();)
	{
		if((*i)->IsState(STATE_DEAD))
                {    
                    IActor * actor = (*i);
                    mActors.erase(i);

                    delete actor;
                    actor = NULL;
                }
		else if ((*i)->IsActive() )
		{
			mActiveActors.push_back((*i));
			 ++i;
		}
		else
			 ++i;
	}
}

Also, for the texture: The same thing for the actor Manager can be done for texture. If you use the same texture for different actor (Let's say you have a lot of clone) there is no need to have the same texture allocated in memory more than once. A texture manager can solve this problem. The actors can then store a pointer to the texture. Be warned that you then can only delete the texture if all actor using it are gone.

 Sprite Background = new Sprite();

'Background' here isn't a pointer (unless Sprite is typedef'd as a pointer), so calling "new" here shouldn't compile.
But 'Background' shouldn't be global anyway, it should probably be a member variable of 'Game'.
And unless required by your API, 'Background' shouldn't be a pointer at all. Just a regular variable (Sprite should handle it's own dynamic memory internally).

In my opinion a programmer should, by default, use memory whose lifetime is automatically managed:
int x = 0;
Sprite sprite;
 
int *ptrX = &x;
Sprite &spriteRef = sprite;
 
struct MyStruct
{
     Sprite background;
     std::vector<Sprite> arrayOfSprites;
};
If I must use manually-managed variables, then I prefer smart pointers.
C++11's smart pointers include: std::shared_ptr, std::weak_ptr, and std::unique_ptr. Each have different uses for different situations. Your compiler will need C++11 enabled to use.
(Don't use std::auto_ptr which is deprecated and no longer recommended for use)
std::shared_ptr<Sprite> sprite = std::make_shared<Sprite>("path/to/image.png");

In very rare occasions, I need to actually manually micro-manage your memory. In those situations, then I use new and delete. In normal hobbyist code, this shouldn't occur too often. It occurs more often when certain APIs enforce the use of new and delete, or when maintaining older code bases or using outdated compilers.

This topic is closed to new replies.

Advertisement