C++ Multiple Calls to Destructor

This is a discussion on C++ Multiple Calls to Destructor within the C++ Programming forums, part of the General Programming Boards category; Hey all,
I'm currently working on creating a resource management system for use in my own projects.
My original idea ...

C++ Multiple Calls to Destructor

I'm currently working on creating a resource management system for use in my own projects.

My original idea was to prevent loading a resource multiple times into memory. For Example: Two objects both need to draw the same image;

I tried to set this functionality up by overriding new and delete.
When a resource is dynamically allocated, it checks to make sure that resource doesn't already exist. If it does, it returns the address of the already existing resource. Otherwise it returns allocated memory.

Obviously I have run into a lot of little tricks with Constructors and initializer lists overwriting already loaded resources, but it works mostly to my liking.

I overwrite the delete to only free the memory if the resource has no more objects referring to it. My current and only issue is that after calling delete and the 'images' destructor from an instance. Any other references to deleting that 'image' do not call the 'images' destructor.

Code:

Image* x = new Image("File1");
Image* x2 = new Image("File1");
// At this Point, x == x2 due to overridden new operator; they reference same file.
delete x; // Calls ~Image() , doesn't free memory for the image
delete x2; // Does not Call ~Image(), does free memory for the image
// Reversing the order, yields the same result. x2 will call the destructor, x will not.

I know there are a lot of wrong practices going on here, but I'm ok with that. Can anyone tell me if a destructor physically marks an object as not being 'that type of object' or something similar?

I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.

I would stick to something like reference counting. If your overridden delete and new operators do that, then you should have no problem keeping track of an Image resource or whatever. And it shouldn't matter what the destructor does as long as delete calls it on time (0 references). Default destructors, in fact, do nothing.

edit - Although I would make the constructors private to avoid circumventing the reference counting.

I would stick to something like reference counting. If your overridden delete and new operators do that, then you should have no problem keeping track of an Image resource or whatever. And it shouldn't matter what the destructor does as long as delete calls it on time (0 references). Default destructors, in fact, do nothing.

edit - Although I would make the constructors private to avoid circumventing the reference counting.

Thanks for the reply.

That's essentially what it does; my concern was any memory that needs to be free in a Resource destructor. For instance if my image has an array of pixel data. I need to delete the pixel data which would be handled within the destructor. There's work arounds, but I would really like to figure out why I am unable to call the destructor of an object twice if I don't release the memory.

Maybe what you could do is this: create a resource registry singleton. Whenever you want to access one of these resources, you pass whatever information is required to the registry, and receive a shared pointer to the resource. The registry itself checks if the lookup information has already been stored, and if so, refers to the corresponding weak pointer. If the weak pointer is expired (or if the lookup information has not been stored, i.e., very first access ever), it creates the resource, stores a weak pointer to it, and returns a corresponding smart pointer. Otherwise, it simply returns a smart pointer from the weak pointer.

I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.

If the weak pointer is expired (or if the lookup information has not been stored, i.e., very first access ever), it creates the resource

There is one case where this might not work. What if the resource system needs to return a default resource instead of creating one? If it is an on demand resource system then it would load the resource but there could be times when say a default resource could be used to notify the user that the requested resource does not exist. This is great for texture resources in graphics systems b/c when you see the default resource pop up you know exactly what has happened. Obviously the answer depends on the requirements and design of the system.

However the resource system that laserlight has so graciously described in great detail is a common design and it works just fine. The simplest answer if you do not want to go through all of that is what whiteflags post talks about. You can use reference counting to keep track of how many refs are floating around out there in space. You can code this on your own which I do not recommend since there are about a million ways to incorrectly code a ref counted object. Besides Boost shared pointers and weak pointers offer the same functionality and you don't ever have to maintain the code behind them which is another win. Boost is free which is also a win. It looks like Boost is a win-win situation here.

I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.

I don't think we can tell from what you have posted. If you want an answer to that, I believe you need to post the smallest possible compilable example.
The approach to this problem is wrong, I do believe, but you've been told that already.