views:

274

answers:

9

I have a simple tank wars style game using the allegro open source library. In my tank class, I initialize arrays of pointers to bitmap objects to 0. Then I create new objects with an allegro function create_bitmap which allocates the memory and initializes it.

Then I go about my business as usual.

The problem is, when I go to release the bitmap memory in the class destructor like a good OO boy, I crash the program because in this specific program, the allegro library does its cleanup (which releases the bitmap objects it created) before the class goes out of scope and is destroyed. It doesn't set my pointers to NULL again though so I can't check if the bitmaps are still valid and if I try to release them they will crash the program.

Is there any way around this? Can I check for valid pointers if they are not NULL? How can I be SURE that the memory is freed if the class is used a different way within the program. As it stands right now, I'm essentially calling new without delete and I don't like it.

+2  A: 

Can I check for valid pointers if they are not NULL?

No. But in your case you don’t need to. Since Allegro promises to take care of its resources you don’t have to (and must not) meddle in the resource handling of Allegro resources. In particular, since you don’t even know how the resources are allocated, you cannot deallocate them.

Konrad Rudolph
Allegro promises to do that at the very end of the program, but If I reuse and delete this tank object several times through the game, each time the bitmaps will not be properly deallocated. BTW, Allegro gives the destroy_bitmap command to remove the resources.
CodeFusionMobile
This seems to indicate that your object is statically allocated. Since the order of construction (and destruction!) of static objects isn’t specified, strive to prevent such a situation. If your object isn’t static the Allegro resources should consequently not be deleted at the end of the scope and everything is fine. I fail to see the problem then.
Konrad Rudolph
+1  A: 

The key to manage memory explicitly is that while you can have several pointers to the same memory area, at any one time, only one of them is the designated owner, while all other only share it. When heap objects own other heap objects, they constitute a tree, rooted in a globally or locally scoped variable somewhere.

You should consider Allegro to be the owner of memory areas you pass into it, and your own pointer just to be a shared pointer, once you return from the initial Allegro call.

And no, barring some allocator trickery, you have no standard way of determining whether memory is valid or not. Allocator trickery can be useful for debugging purposes, but don't mess with the internals of a library.

Pontus Gagge
What if I just set all my pointers to NULL in the dtor? Then I've done my part and it's on allegro if they aren't cleared?
CodeFusionMobile
Yes, that's also good practice at least in debug code, to trigger errors through invalid pointers to the holder early.
Pontus Gagge
@Pontus Is it good practice beyond debug code?
CodeFusionMobile
It's not bad per se, but it wastes a CPU cycle or two if your program is correct... It depends on your philosphy towards the ordinary meaning of defensive (http://en.wikipedia.org/wiki/Defensive_programming) vs fail fast (http://en.wikipedia.org/wiki/Fail-fast) programming. In debug mode, I favour fail fast; in production code, I tend to favour fault tolerance (depending on how badly the system can screw things up later if it is tolerant right now).
Pontus Gagge
+1  A: 

Sounds like a rather horrible leaky abstraction

You cannot hope to find a safe method of destroying memory if you do not know exactly how it has been allocated. The cleanup function sounds like it's there for a reason and doing a job - just gotta live with it.

You could of course wrap these bits and include some documentation in comments so that other developers don't fall into the same trap.

Also profile your application to make sure there's no leaking.

Ali Parr
A: 

Are you sure you are using this correctly? I have dug out some of my old Allegro code and I have a constructor with a create_bitmap call and a destructor with a release_bitmap call and it worked fine.

I don't remember anything about Allegro releasing memory for you automatically. Are you accidently overwriting the pointer with some non-memory value? Is there another place where this pointer is being freed?

Matt Breckon
There is, I've added as much to the OP. destroy_bitmap(BITMAP*) is what I am trying to use in the dtor. Also, as I said before, Allegro attempts to automatically do it before the dtor even gets called which is the root of my problem.
CodeFusionMobile
Correction, I tried to add it to the OP, SO isn't responding to my edits for some reason.
CodeFusionMobile
Are you sure Allegro is releasing the memory? Are you accidentally overwriting the pointer or releasing the bitmap elsewhere?
Matt Breckon
@Matt B, there is no place at which the bitmaps are reassigned. Before the call to Allegro_exit() they are fine and after they are gone.
CodeFusionMobile
+4  A: 

You shouldn't be using arrays of raw pointers. Allegro comes with a create_bitmap and a destroy_bitmap function. That maps very nicely to the C++ concept of constructors and desctrutors. You should have an AllegroPlusPlus::bitmap class, which manages exactly one bitmap. Your Tank class can tehn simply have an array of those.

This is a seperation of responsibilities. The tank class should not know too much about bitmaps and their memoy management, and the bitmap class should handle precisely one bitmap.

You want to recycle bitmaps in your Tank class. This is no problem; it can be done easily with a good implementation of bitmap::operator=(bitmap const&) or other overloads. But again, make that assignment a responsibility of the bitmap class, not the tank class.

MSalters
SO is not responding when I try to edit the post so I'll put it here. This is homework, and being such adding classes outside the instructed parameters is not good.
CodeFusionMobile
Adding classes in a C++ class is not allowed? WTF? If you can't make a new class as part of the assignment how do you have a dtor with a problem?
jmucchiello
@jmucchiello The class I have a dtor problem with I was instructed to create. I got scorned on the previous assignment for using topics too advanced for the class I'm in.
CodeFusionMobile
+1  A: 

When is the destructor being called? Is it after the Allegro library has been shut down? If so then can you delete all the objects first?

Matt Breckon
A: 

Use reference counting when dealing with multiple pointers to heap memory. Basically, if you have multiple references to the same memory and delete one, the other reference might still think it exists.

http://en.wikipedia.org/wiki/Reference%5Fcounting

Rantaak
+3  A: 

I think the problem is not that allegro releases the bitmaps itself (or otherwise you wouldn't need to release them at exit) but that allegro library has been deinitialized before the destructor is called.

int main()
{
    ObjectManagingBitmaps o;
    ...
    return 0;
    //allegro automatically shut down here
} //o destructor invoked here
END_OF_MAIN()

What you can do to ensure that the destructor is invoked first is to use an artificial scope:

int main()
{
    {
    ObjectManagingBitmaps o;
    ...
    } //o destructor invoked here
    return 0;
    //allegro automatically shut down here
} 
END_OF_MAIN()
UncleBens
Is his object even in main? I'll bet his object is global.
jmucchiello
That would be rather unnice. In such case the global can be a pointer which is deleted before main ends, or you might provide a deallocation method (and perhaps check for NULL in the destructor). Or ... just let the OS clean up allocations if this object is meant to be used as a single global/singleton.
UncleBens
My object is local to main. Before returning, allegro requires a call to allegro_exit() which deinitializes the library. Then the object goes out of scope when main returns. Everything you said is accurate.
CodeFusionMobile
A: 

I'd say you should ensure that your objects are all destructed before shutting Allegro down, you can do this easily enough by (if they're on-stack) closing the scope they exist in before shutting down Allegro.

If you need to shut down Allegro earlier than this (e.g. because of a fatal error) then you could just call exit in which case no destructors get run (but your program still won't crash).

Don't spend too much time making sure that the program cleans up on exit, save your effort on making sure it doesn't leak while it's running :)

MarkR