Woah there, std::map has find which returns interator and has log n complexity and you're doing n complex linear search. Also erase returns iterator to element after erased one so you don't have to do --i. You might also want to use make shared(because it allocs shared ptr and the class in one go, as opposed to new) and maybe some weak pointers.
Wall of text(I'm not liable for any (brain) damage that might happen as a result of reading my opinions):
I once seen singletons(which were very similar to doom 3 way of doing globals) suggested in a book but I was kind of against globals and singletons but after reading doom 3 I'm leaning towards well thought out (possibly pimpl'd) pointers to some systems. Globals and singletons seem to be really hated by many programmers,
it's one of these religious wars, akin to Java vs. c++ and C vs. c++, you are the programmer and your program belongs to you, you must make the decision to go with what you think is best and then learn from your mistakes.Like what doom 3 does with most/all of it's major system is make them all pure virtual and then make extern pointer and assign it in .cpp file. And they all are alive for entire duration of the program but have Init and Shutdown methods. And the pointer can in theory be assigned to something else if you want to override that system with your own(I don't know if that's part of the reason it's pointer and not reference).
Also cvars are global variables exposed to the dev console(basically). There is also a funny thing going on with cvars and how extern ptr in one header maps to different variables in different dlls but I won't get into that
.
The thing is that pretty code is good but working code is even better. I've seen games coded in hsp, it has
only globals. Tons of games use globals. Doom 3 does it quite well actually, it doesn't globalize render world, or render window or parsers for script, only main program script of the game is globalized. Carmack said he is not familiar with entire codebase, just that he laid out rough sketch for entire engine = that way of use of globals is very tight and doesn't leak abstractions or make things non localized, that is quite great and beats one of reasons against using globals. And it actually makes sense to use globals like that. Imagine you had settings screen that lets user choose difficulty, you'd probably stuff that variable in a config file or something similar and basically use filesystem like a global variable dump except it doesn't feel nearly as dirty. Singleton is to me just bastardized global(I know it's supposed to be something more), it usually ends up as that, 'its not global, its a singleton, im a good programmer
', the way doom 3 does it is actually like a singleton(the entire derived class is in .cpp, you can't instance another one) that is swapable(extern ptr) and doesn't require recompilation of every file that includes it's header to perform that swap(because it's a pure virtual interface), sounds rather good, right? Globals are so so useful when used right, but people have silly ideas(at my old college someone wanted to use global to return 2 values from function).
Doom 3 is really quite ingenious engine, it's c++, no eye bleeding templating, not overly using globals, configurable, scriptable, exstensible, open source and it's bearable for visuals even today, aged well, and just around 600k of code. Just wow, right?
I at one point thought that cvars had a problem in them: the local ones in functions that get destroyed at scope exit would not remove themselves from linked list. Then I read a bit more and found that the linked list of cvars ceased to exists after cvar system is init and takes the variables and copies them so the actual cvars are now pointers/handles to internal ones, managed by cvar system. And when is the cvar system init? BEFORE any function can be called, so BEFORE any local cvar can be created. This is perfectly thought out.
Globals often have following pitfalls listed:
1. Non locality of code.
2. Code is harder to test.
3. Initialization order is pretty much random for globals in different files(famous global sf::RenderWindow crash).
4. There is only one instance of singleton, what if you want more?
5. Concurrency.
1 is kind of invalidated as soon as you only globalize doom style the huuuge systems, like the filesystem
std::ifstream file("hey.txt"); turns into myengine::filehandle f=filesys->getFile("hey.txt"); and so on, it was pretty global-ish to begin with and you just sprinkle some additional info like list of directories to look for "hey.txt" in. For many things like opengl, openal, filesystem, you'd use global functions anyway to enforce some sort of state for them to be in, there is one graphics card, one audio output, you can have few windows or sounds but do you need few texture or audio buffer managers and have few copies of every resource? Lying side by side on same ram/gpu, wasting space? No.
2 and 3 are completely void because of the way doom 3 works. You can pretty much make a testing subsystem for every class and assign global pointer to that before initing. The global systems classes do
nothing heavy on normal ctors and dtors and init and deinit order is tightly controlled by your explicit function calls so you do it in right order. That + assert(whateversystemineed->isInit()) when a major subsystem relies on another one being initalized before. In case of cyclic dependancies: don't init all at once.
5 is just eh.. that's what atomics and mutexes are for and this is not issue with just globals and singletons, things that are intrinsically thread safe are rare and far between so it's up to the programmer to make them safe and very very often the app is single threaded anyway.
4 is sort of anti-yani(you ain't gonna need it) pattern. Do you really need two filesystems? Two dev consoles? Two instances of your entire game? It could be cute and proof how global-less your code is to run two games at once in one exe but the thing is you won't ever use that 'freedom' to have more than one of whatever it is you are making global or singleton because this is not your goal.
Bottom line: do it doom 3 way, globalize only MAJOR sub systems and don't use filesystem or struct of pointers to 20 subsys classes that you pass everywhere instead of a properly implemented globals like that.