Overusing smart pointers?

Started by
31 comments, last by sundersoft 11 years, 10 months ago
I have another issue with my resource manager that I want some comments on..

I have been using shared_ptr for this previously..

The options I have been thinking of are these:

1. Have a template<typename ResourceT> class ResourceManager; and have a function Hash register(const std::string& filename); on that class where I can register to a resource with the given filename. If the resource does not exist the register function will create the resource and store it on the stack in a unordered_map. I would also store a reference count and the user would need to call unregister when its done with the resource and it would be deleted when ref count is 0. To get the resource I would call ResourceT& GetResource(Hash resourceHash); I could then store raw pointers to the objects as they cannot be deleteded before I call the Unregister function. I like this approach as I try to follow the advice on keeping things on the stack mentioned in this thread..

2. I could have the same ResourceManager class as above, but I could store weak_ptr's in the unordered_map and when I do register it would check if theres a weak_ptr that I could get a valid shared_ptr to the object or it can create a new shared_ptr with the object, store a weak_ptr and then return the shared_ptr.. with this I dont have to worry about calling unregister and I can run a cleanup of the expired weak_ptr's whenever I want to..

Why should I choose one of these over the other, or even something else?
Advertisement
Personally, I'd use the weak pointer in map. I hate doing manual cleanup, like calling an "unregister" method. Having the first suggested setup, I'd probably wrap it into an object somehow that automatically called that method on destruction.

Another question is, would you really have to release a resource when nothing's using it? If it's not a huuuge game, you could load everything on startup and just let it sit there until shutdown. No reference counting, because you guarantee the lifetime of the resources for the entire program's lifetime. You wouldn't want something to reload a resource all the time in the middle of a game.

I have another issue with my resource manager that I want some comments on..

I have been using shared_ptr for this previously..

The options I have been thinking of are these:

1. Have a template<typename ResourceT> class ResourceManager; and have a function Hash register(const std::string& filename); on that class where I can register to a resource with the given filename. If the resource does not exist the register function will create the resource and store it on the stack in a unordered_map. I would also store a reference count and the user would need to call unregister when its done with the resource and it would be deleted when ref count is 0. To get the resource I would call ResourceT& GetResource(Hash resourceHash); I could then store raw pointers to the objects as they cannot be deleteded before I call the Unregister function. I like this approach as I try to follow the advice on keeping things on the stack mentioned in this thread..

2. I could have the same ResourceManager class as above, but I could store weak_ptr's in the unordered_map and when I do register it would check if theres a weak_ptr that I could get a valid shared_ptr to the object or it can create a new shared_ptr with the object, store a weak_ptr and then return the shared_ptr.. with this I dont have to worry about calling unregister and I can run a cleanup of the expired weak_ptr's whenever I want to..

Why should I choose one of these over the other, or even something else?


If you're going to have register and unregister functions in one of your classes, you should consider providing a helper RAII class which registers its argument in its constructor and unregisters it in the destructor. Anyway, if you're going to be using reference counting you might as well just use shared_ptr since the efficiency improvement of implementing it yourself is going to be negligable.

Anyway, the implementation you should use is going to depend on what kind of efficiency constraints you want and what exactly you're trying to do. For example, if you're making a game and you want to cache textures and models that were loaded from the disk, I'd recommend the following in your caching class:

-Have it return shared_ptrs on an object lookup. This allows you to not have to worry about whether the cached objects are in use or not when you flush the cache. I would also recommend storing shared_ptrs in the cache because, if you use weak_ptrs, then the object would become flushed from the cache as soon as the last shared_ptr reference to it was destroyed. This would be bad if part of your game involved loading a model (e.g. of a rocket), instancing it, and then destroying it later. Every time the model would be requested from the cache, it would have to be loaded from disk again.

-Have a "flush" function which goes through the cache and removes any object that only has one shared_ptr referring to it (i.e. the one stored by the cache). This removes all currently unused objects from the cache. It is not desireable to remove every object from the cache because this may cause two different instances of the same object to exist.

When you're performing an operation where most of the cache would be redundant (e.g. loading a new level), you would flush the cache. For loading a level, you would first delete the existing one, load and initialize the new one, and then flush the cache. This order would cause any resources that exist in both levels to not need to be loaded from disk twice.

This topic is closed to new replies.

Advertisement