Singleton Design

Started by
11 comments, last by Slavik81 12 years, 10 months ago
The logger will be destroyed sometime during returning from main. It is safe to use it in the destructor of App, because that is called inside main(). It is not safe to call it in the destructor of other Singletons, unless they are deterministically destroyed before main() ends.

Running code outside main() is dubious.

One simple alternative is to make all these objects local to main. Then you can point global pointers at them when they are valid. Before they are destroyed, null out the pointers. To make it safe ensure that all your "global" classes do not use the global pointers to other sub-systems. Instead, they must be passed pointers/references. This way your constructor order will naturally force objects to be constructed in a particular order, and will alert you to circular dependencies (your Logger shouldn't be using the MemoryManager if the MemoryManager wants to use the Logger).


Logger *globalLogger = 0;
MemoryManager *globalMemoryManager = 0;

int main()
{
Logger logger("foo.log");
MemoryManager memoryManager(logger);

// Globals accessed before here will be NULL!

globalLogger = &logger;
globalMemoryManager = &memoryManager;

// Globals are safe!

Application application;
application.run();

// Goodbye globals...

globalLogger = 0;
globalMemoryManager = 0;
}

You may want to export your globals through functions rather than directly as global pointers to prevent accidental assignment:

namespace
{
Logger *globalLogger = 0;
MemoryManager *globalMemoryManager = 0;
}

Logger &logger()
{
assert(globalLogger);
return *globalLogger;
}

MemoryManager &memoryManager()
{
assert(globalMemoryManager);
return *memoryManager;
}

int main()
{
// ...
}
Advertisement
Going off topic, but when you least expect it, the linker is going to dead-strip that code and it's not going to make it into the final EXE, leaving you wondering why auto-registration has suddenly stopped working for certain classes...


The ugliest part is where compilers are allowed to delay the initialization of static variables until they are actually read for the first time, which makes static factory registration such a ridiculously messy thing.


static bool isRegistered = register<X>();


Looks simple and obvious, yet the compiler ignores the importance of all side effects in register() and decides "I won't do that until somebody reads isRegistered, even if that is never". Even a working version using registration objects stopped working when putting that code into a library. It was in the lib, but got stripped when building an application using that lib (actually that part worked, until you built an app using a dll using the lib).
f@dzhttp://festini.device-zero.de

[quote name='Hodgman' timestamp='1306733968' post='4817393']Going off topic, but when you least expect it, the linker is going to dead-strip that code and it's not going to make it into the final EXE, leaving you wondering why auto-registration has suddenly stopped working for certain classes...


The ugliest part is where compilers are allowed to delay the initialization of static variables until they are actually read for the first time, which makes static factory registration such a ridiculously messy thing.


static bool isRegistered = register<X>();


Looks simple and obvious, yet the compiler ignores the importance of all side effects in register() and decides "I won't do that until somebody reads isRegistered, even if that is never". Even a working version using registration objects stopped working when putting that code into a library. It was in the lib, but got stripped when building an application using that lib (actually that part worked, until you built an app using a dll using the lib).
[/quote]
Will that happen with statics created within a function as well?

This topic is closed to new replies.

Advertisement