Efficient usage of a c++11 shared_ptr in an asset manager
c++, c++11, opengl, shared-ptr
Solution
Instead of storing `shared_ptr`s in the asset manager's `map`(see below, use a regular map), store `weak_ptr`s. When you construct a new asset, create a `shared_ptr` with a custom deleter which calls a function in the asset manager which tells it to remove this pointer from it's map. The custom deleter should contain the iterator into the map of this asset and supply that iterator when telling the asset manager to delete it's element from the map. The reason a `weak_ptr` is used in the map is that any subsequent requests for this element can still be given a `shared_ptr` (because you can make one from a `weak_ptr`) but the asset manager doesn't actually have ownership of the asset and the custom deleter strategy will work.
Edit: It was noted below the above technique only works if you use a `std::map` not a `std::unordered_map`. My recommendation would be to still do this, and switch to a regular map.
Problem
I'm working on a game (and my own custom engine). I have quite a few assets (textures, skeletal animations, etc.) that are used by multiple models and therefore get loaded multiple times. At first, my ambitions were smaller, game simpler and I could live with a little duplication, so `shared_ptr` which took care of resource cleanup once the last instance was gone seemed like a good idea. As my game grew, more and more resources got loaded multiple times and all the OpenGL state changing slowed the performance down to a crawl. To solve this problem, I decided to write an asset manager class. I'm using an `unordered_map` to store a path to file in `std::string` and c++11's `shared_ptr` pointing to the actual loaded asset. If the file is already loaded, I return the pointer, if not, I call the appropriate `Loader` class. Plain and simple. Unfortunately, I can't say the same about removal. One copy of the pointer remains in the `unordered_map`. Currently, I iterate through the entire map and perform `.unique()` checks every frame. Those pointers that turn out to be unique, get removed from the map, destroying the last copy and forcing the destructor run and do the cleanup. Iterating through hundreds or thousands of objects is not the most efficient thing to do. (it's not a premature optimization, I am in optimization stage now) Is it possible to somehow override the shared pointer functionality? For example, add an "onLastRemains" event somehow? Maybe I should iterate through part of the `unordered_map` every frame (by bucket)? Some other way? I know, I could try to write my own reference counted asset implementation, but all current code I have assumes that assets are shared pointers. Besides, shared pointers are excellent at what they do, so why re-invent the wheel?