Sign in to follow this  

Cleanup After C++

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

Hello Just a quick question... Why is it that I keep reading how important it is to release objects, etc. before your game exits? Surely when the game exits ALL memory would be cleared? ie. that is handled by Windows I can understand why you'd want to clean up during the runtime of the game, so you don't get a memory leak and slowdown... but why, if you're going to quit anyway, do you need to release stuff? The only thing I can think of is that maybe when you release a texture, for example, it tells the video card to unload that texture, etc. Is that it? For the video card? (obviously I'm used to working with languages with garbage collection like Java and C# lol)

Share this post


Link to post
Share on other sites
If your game exits and the OS that runs your game releases all memory and handles (which a proper OS should), then you shouldn't worry about releasing all these things by yourself.

On most consoles your game would never exit anyway (without some sort of reset), so you don't have to worry about it at all.

However it is good practice to release everything manually [in Debug builds]. Often just to check whether you have a leak somewhere in your code.

Share this post


Link to post
Share on other sites
In C++ when an object is deleted, its destructor is called. If you simply exit the program suddenly, then the memory will be freed (by the OS), but the destuctors will not be called.

In a simple program, the destructors may just be cleaning up memory, in which case it would be safe to simply call exit and let the OS clean up your mess.
But in a more advanced program, often the destructor will have important tasks to complete, such as releasing graphics handles, closing files, writing info to a data-base, terminating a sound loop, etc, etc...


If a program uses the RAII idiom (which many will argue is a very good practice in C++ programming), then respecting the destructor is imperative!

Share this post


Link to post
Share on other sites
"such as releasing graphics handles, closing files, writing info to a data-base, terminating a sound loop"

Ah, ok - so graphics handles... I've seen some games that, when exited incorrectly, can prevent you from opening another DirectX program until you restart your computer.

Is this why?

Share this post


Link to post
Share on other sites
Quote:
Original post by NightCabbage
Ah, ok - so graphics handles... I've seen some games that, when exited incorrectly, can prevent you from opening another DirectX program until you restart your computer.

Is this why?
That would be an example of why (or at least I suppose it is - I don't actually know that that particular problem is caused by a failure to release or clean up resources).

In any case, if your C++ coding practices are halfway decent, everything will get cleaned up automatically no matter what, which begs the question...how exactly are you getting yourself into a position where it's possible to leave resources unclaimed when the application terminates? In other words, how are you managing your dynamic resources, and what sorts of resources are you tempted to not clean up?

Share this post


Link to post
Share on other sites
I'm more wondering how to figure out WHAT needs cleaning up after :)

eg. if I make a texture, do I need to clean it up?

How about a game object... like a player or some such

(they're just examples but you get the idea)

How do you know what needs cleaning up?

Share this post


Link to post
Share on other sites
You should clean up absolutely everything you allocate!
Why? Because then when you have a problematic memory leak it will be a piece of cake to spot it using various leak detection tools. If it's hidden amoungst 1000's of other leaks then you'll have a hard time finding it.

The best way is to not write code that can leak to begin with.

Share this post


Link to post
Share on other sites
Ok, I guess I just need to get my head around cleaning stuff up :)

So if I make anything, a string, an array, a texture, a struct, etc. I have to clean it up, right?

(by setting it to NULL? or calling the destructor? or just when it loses scope?)

Share this post


Link to post
Share on other sites
Let's clear something up. Your program *should* clean up after itself and not leave it to the OS. But it doesn't have to. If it fails to clean up, the OS will correctly close all handles and release all the memory. Otherwise this would be a very serious bug in the OS as resources are leaked and lost forever until the system reboots.

The only reason you should clean up after yourself is good practice - don't get into the habit of leaving it and assuming something else will clean it up later. And, as iMalc said, it also assists in debugging.

When people say "clean up" they mean releasing all resources (such as a texture) and deleting all memory that you've allocated.

Share this post


Link to post
Share on other sites
Quote:
Original post by NightCabbage
Ok, I guess I just need to get my head around cleaning stuff up :)

So if I make anything, a string, an array, a texture, a struct, etc. I have to clean it up, right?

(by setting it to NULL? or calling the destructor? or just when it loses scope?)


Anything new'ed should be delete'd (or, in case you're using C functions, the (almost) equivalent is malloc, realloc and free).

new automatically calls the constructor, and delete calls the destrutor - no need to do that yourself.

When you ask D3D or OGL to make room for a texture, or a third API to make room for any other data, you should also inform the proper API, if such a method exists within that API, when you don't need that room anymore.

Share this post


Link to post
Share on other sites
Quote:
Original post by NightCabbage
Ok, I guess I just need to get my head around cleaning stuff up :)

So if I make anything, a string, an array, a texture, a struct, etc. I have to clean it up, right?

(by setting it to NULL? or calling the destructor? or just when it loses scope?)

To simplify things, I'm gonna pretend that all memory is either allocated on the stack (local) or the heap (dynamic). If you don't know about the stack and the heap, get yourself an "Intro to operating systems" book.

In Java, when you create a new object, the object itself is created on the heap, and your local variable (which references that new object) exists on the stack. Stack variables are local, so they can go out of scope. When your local reference goes out of scope, there are no more references to the object, so Java will automagically delete the object from the heap for you.

In C++, things are a bit trickier - you can choose to put objects on either the stack or the heap! Also, the language doesn't keep track of heap objects for you, which means if you don't have a pointer (C++ pointer = Java reference) to the object anymore, it will be leaked.

If you create an object on the stack, it will clean itself up when it goes out of scope. If you create an object on the heap, it will remain there until you delete it.


class Object
{
Object() { /*constructor code*/ }
~Object() { /*destructor code*/ }
void DoSomething() {}
};

void CreateOnStack()
{
if( true )
{
Object bob;//this creates a new object on the stack and calls the constructor
bob.DoSomething();//When using an object directly, use the "." operator
}//the object goes out of scope here and automatically calls the destructor
}

void CreateOnHeap()
{
//N.B. This is a 'pointer' to an object (because of the *)
Object* bob;//this creates a local variable which *can* contain a memory address of an object
bob = new Object;//this creates a new object on the heap, calls the constructor and stores it's address in 'bob'
bob->DoSomething();//When using an object through a pointer, use the "->" operator
delete bob;//this calls the destructor, and releases the memory from the heap
bob = NULL;//bob still contains the memory address that the object was using, but the object is now deleted! So it's a good idea to throw away that old address by setting it to NULL!
}

void CreateLeak()
{
if( true )
{
Object* bob = new Object;//Create object on heap, call constructor
}
//Oh no! Our pointer has gone out of scope, but the object still exists on the heap. We no longer know what it's address is, so we can't possibly delete it!
}

void CreateDanglingPointer()
{
Object* bill = NULL;//If a pointer hasn't been set to an object yet, it's a good idea to set it to NULL/zero, so that you know it's not pointer to anything!
if( true )
{
Object* bob = new Object;//Create object on heap, call constructor
bill = bob;//copy the address into bill as well
delete bob;//Call destructor, free memory
bob = NULL;//Good practice
}
bill->DoSomething();//This will crash! As bill contains the memory address of an object which has been deleted!
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
If a program uses the RAII idiom (which many will argue is a very good practice in C++ programming), then respecting the destructor is imperative!


NightCabbage: you must Must MUST(!) pay attention to what Hodgman said, here! In C++ scope does the equivalent of garbage collection in Java.

Cleanup in C++ should be as automatic as it is in Java or any other garbage collected language, but it is different.

If you allocate memory in a constructor, you deallocate in it a destructor. You also make sure copying is defined correctly[1]. This is one of the differences between C++ and Java, say -- that of preferring value semantics over reference semantics.

You will find, however, that a lot of the work has been done for you already. By using the standard library containers such as std::vector instead of arrays, std::list instead of a hand-crafted linked-list and so on, you shouldn't even need to think about allocation, deallocation or clean-up of any kind. Similarly, if you ever 'new' anything[2], put it in to a smart pointer or some kind of resource-managing object straight away. Cleanup is then done implicitly by the smart pointers destructor.

If you get in to the habit of using RAII generously, you will find that writing exception safe code is actually easier than it is in Java or C# ('using'/IDisposable was invented to allow an approximation of RAII in C#). If you miss a 'finally' clause in a Java program for example, you could have a long standing resource leak (such as a database connection or OpenGL texture) that will only get reclaimed on the next collection which is at who-knows-when.[3]

[1] Look up the rule of three
[2] There is the odd case where keeping the raw pointer is a good idea. But they are extremely rare, at least in my experience.
[3] I'm not slamming C# or Java here. I use them, too. But RAII is a powerful tool, not an inconvenience.

Share this post


Link to post
Share on other sites
Thanks guys! You're helping me a lot! :D

One quick question...

I'm having a problem where I need to declare a global variable (a pointer to an object) and then initialize it in the body of my code...

Here's what I'd do in java:


Animal dog;

public main()
{
int legs = 4;
dog = new Animal(legs);
}






Now this makes sense to me.

And I know I can do it the same way in C++.
(the only difference being I'd declare Animal* dog; instead)

But I also wonder if I should be able to do it another way (without using new)?

Or maybe not, now that I think about it.

So a local declaration (in scope) puts the object on the stack, and the new keyword puts it in the heap - is that right?

So in this case, I have to put it on the heap, because there's no scope for it.
And I just have to manage the variable myself (eg. here's where a leak could possibly occur)

Am I getting it now? :)

[Edited by - NightCabbage on November 26, 2007 8:42:36 PM]

Share this post


Link to post
Share on other sites
Quote:
So in this case, I have to put it on the heap, because there's no scope for it.


Hmm, semantics and details.

The pointer itself is allocated on stack, or stored in registers. It persists as long as the variable remains in scope. After it's gone, the destructor gets called (pointless for basic types) and the memory is "de-allocated" (stack is popped).

The memory you allocated with new however remains in use until application remains alive.

A more practical example would be this:

public main()
{
int legs = 4;
Animal dog(legs);
} // dog's destructor is called
// int destructor is called (not really)



When allocating on stack, you do not need to worry about destruction. Basic types have destructor semantics fully supported (for templates), although they don't do anything and won't actually be called (you can call them though)



Your example however is wrong. What you do is allocate an Animal named dog in global scope, then try to assign it a pointer. This will fail unless animal overloads such assignment operator.

Animal *dog; // pointer to dog, dog doesn't exist yet

public main()
{
int legs = 4;
dog = new Animal(legs); // a new instance of dog is created
}
// you have leaked memory, namely the Animal you allocated



Simple rule - if you call new, you must call delete. Once for each.

Quote:
Here's what I'd do in java:



typedef boost::scoped_ptr<Animal> AnimalPtr;

public main()
{
int legs = 4;
AnimalPtr dog( new Animal(legs));
}// last reference to dog is gone, deallocate memory



Using smart pointers, you can use same methodology as in memory managed languages.

Share this post


Link to post
Share on other sites
Quote:
Original post by NightCabbage
But I also wonder if I should be able to do it another way (without using new)?


If you really need dog to have global scope:


Animal *dog;

int main()
{
Animal Dog(/*whatever*/);
dog = &Dog;

//OR

std::auto_ptr<Animal> Dog( new Animal(/*whatever*/) );
dog = Dog.get();
}





In either case, dog will be cleaned up when Dog goes out of scope (i.e., when main exits).

Share this post


Link to post
Share on other sites
Antheus - "your example is wrong"

Did you mean this example?


Animal dog;

public main()
{
int legs = 4;
dog = new Animal(legs);
} // dog still exists




(that was in Java)

in C++ it would be:


Animal* dog;

public main()
{
int legs = 4;
dog = new Animal(legs);
} // dog still exists




Is that what you meant?

Driv3MeFar - thanks, that was what I was wondering :)

Share this post


Link to post
Share on other sites
Quote:
Original post by NightCabbage
Surely when the game exits ALL memory would be cleared?

ie. that is handled by Windows


Only if you are ignoring Win9x/ME since those systems won't clean up anything for you.

There really is no point in hoping that the user runs an OS that cleans up your mess when you're perfectly capable of doing it yourself.

Share this post


Link to post
Share on other sites
Quote:
Original post by SimonForsman
Quote:
Original post by NightCabbage
Surely when the game exits ALL memory would be cleared?

ie. that is handled by Windows

Only if you are ignoring Win9x/ME since those systems won't clean up anything for you.

I'm finding this very difficult to swallow. I can't seem to find proof one way or another, but Windows 95 ran in 32-bit protected mode. It's almost difficult not to release memory for a 'sandboxed' program when it terminates. Not to mention the volumes of empirical evidence from my days developing substandard software on Windows 98 [rolleyes].

I know that system-memory-leaks were common in DOS, and I could believe the same for Windows 3.1 as it recycled a lot of DOS code (even though it ran in protected mode). So... citation needed?

Share this post


Link to post
Share on other sites
9x did clean up for you, however it's drivers were notorious for leaking if not properly terminated. So you wouldn't leak from simple allocations, but if you had a shoddy graphics or sound driver you could leak resources from them. 2k/XP seem a lot better in terms of having a stable driver architecture so you get less of that.

Win 3.1 was a whole different ballgame, due to the way it worked there was minimal memory protection between apps and minimal cleanup on exit. It was very easy to leak most of your memory away from a single misbehaving app.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jerax
9x did clean up for you, however it's drivers were notorious for leaking if not properly terminated. So you wouldn't leak from simple allocations, but if you had a shoddy graphics or sound driver you could leak resources from them. 2k/XP seem a lot better in terms of having a stable driver architecture so you get less of that.

Win 3.1 was a whole different ballgame, due to the way it worked there was minimal memory protection between apps and minimal cleanup on exit. It was very easy to leak most of your memory away from a single misbehaving app.


you're probably right, was a long time since i used those systems.

Share this post


Link to post
Share on other sites
You're missing one important reason why applications should do the right cleanup. DirectX is based on COM model, which means you have one instance of object referenced by (possibly) multiple applications. To make story short: it is possible that when you call some method, the code executes somewhere in a different process. That process is doing the bookkeeping required to serve several callers (ref counting) and it is possible that it has some data allocated to handle your application. So not releasing COM interfaces may lead to memory leaks in different processes. It is also possible that GDI objects used by your application are allocated in the explorer process. Results would be the same.

Share this post


Link to post
Share on other sites

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