5.5 Singleton

The problem addressed here is that one may have a class that one only wants to have one single, easily accessible instance of.

The solution, as shown in Figure 5.7, is to give the Singleton class a single static Singleton *_pinstancncesingleton member. Initially this member is NULL. You give the Singleton class a public static accessor pinstance() that (a) initializes pinstancncesingleton if it's still NULL and (b) returns pinstancncesingleton. An additional wrinkle is that, in order to prevent the users of this class from making additional Singleton instances, you make the Singleton constructor private.

Figure 5.7. The Singleton pattern


The static Singleton *_pinstancesingleton pointer instance resides in singleton.cpp and is initially set to NULL with a line like this.

Singleton* Singleton::_pinstancetancesingleton = NULL; 

So when does _pinstancesingleton get initialized? The trick is to have pinstance() initialize it the first time it's called. That is, we use code like the following.

Singleton* Singleton::pinstance() 
    if (_pinstancesingleton == NULL) 
        _pinstancesingleton = new Singleton(); 
    return _pinstancesingleton; 

In the Pop Framework, the cRandomizer class is a Singleton class. There are a large number of useful cRandomizer methods for returning random integers, reals, vectors, colors, and so on. In order to call these methods, we need a cRandomizer object to call them. The methods that return random values can't be static because the internal state of the cRandomizer object is changed by each of these calls. The internal state has to change so that the cRandomizer doesn't repeat itself any sooner than necessary.

An annoying but seemingly unavoidable side-effect of having a pointer singleton instance Singleton *_pinstancesingleton is that we need to have the app remember to delete this instance at exit. In the case of the cRandomizer, we have a static cRandomizer::deleteSingleton() method that we call in the CPopApp destructor. (In Java or C#, with automatic garbage collection, this isn't an issue you'd have to worry about.) In most cases, deleting this little Singleton object isn't really that important, since you are, after all, terminating the program, at which time any remaining memory is freed up anyway. But the Visual Studio debugger does give you a nagging warning if you fail to free all your memory, so it's just as well to do the right thing.

Why not just have the static Singleton member be an instance that we declare as Singleton Singleton::_instancesingleton;? And then go ahead and in-line the pinstance method as Singleton::pinstance(){return &(Singleton::_instancesingleton);}?

If we took this approach with cRandomizer, it would in fact work in most versions of the Pop Framework. But the approach is risky. The risk has to do with the fact that in a multi-file C++ project, the programmer has no sure control over the order in which the static objects declared in the various *.cpp modules get initialized. So it's possible that the constructor of some static object might call Singleton::pinstance() before the Singleton::_instancesingleton got initialized, and disaster would ensue.

By using the Singleton::_pinstancesingleton, we can make a fail-safe pinstance() that expressly initializes _pinstancesingleton the first time it's called.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference