I l@ve RuBoard Previous Section Next Section

6.2 The Basic C++ Idioms Supporting Singletons

Most often, singletons are implemented in C++ by using some variation of the following idiom:



// Header file Singleton.h


class Singleton


{


public:


   static Singleton* Instance() // Unique point of access


   {


      if (!pInstance_)


         pInstance_ = new Singleton;


      return pInstance_;


   }


   ... operations ...


private:


   Singleton(); // Prevent clients from creating a new Singleton


   Singleton(const Singleton&); // Prevent clients from creating


                       // a copy of the Singleton


   static Singleton* pInstance_; // The one and only instance


};





// Implementation file Singleton.cpp


Singleton* Singleton::pInstance_ = 0;


Because all the constructors are private, user code cannot create Singletons. However, Singleton's own member functions—and Instance in particular—are allowed to create objects. Therefore, the uniqueness of the Singleton object is enforced at compile time. This is the essence of implementing the Singleton design pattern in C++.

If it's never used (no call to Instance occurs), the Singleton object is not created. The cost of this optimization is the (usually negligible) test incurred at the beginning of Instance. The advantage of the build-on-first-request solution becomes significant if Singleton is expensive to create and seldom used.

An ill-fated temptation is to simplify things by replacing the pointer pInstance_ in the previous example with a full Singleton object.



// Header file Singleton.h


class Singleton


{


public:


   static Singleton* Instance() // Unique point of access


   {


      return &instance_;


   }


   int DoSomething();


private:


   static Singleton instance_;





};


// Implementation file Singleton.cpp


Singleton Singleton::instance_;


This is not a good solution. Although instance_ is a static member of Singleton (just as pInstance_ was in the previous example), there is an important difference between the two versions. instance_ is initialized dynamically (by calling Singleton's constructor at runtime), whereas pInstance_ benefits from static initialization (it is a type without a constructor initialized with a compile-time constant).

The compiler performs static initialization before the very first assembly statement of the program gets executed. (Usually, static initializers are right in the file containing the executable program, so loading is initializing.) On the other hand, C++ does not define the order of initialization for dynamically initialized objects found in different translation units, which is a major source of trouble. (A translation unit is, roughly speaking, a com-pilable C++ source file.) Consider this code:



// SomeFile.cpp


#include "Singleton.h"


int global = Singleton::Instance()->DoSomething();


Depending on the order of initialization that the compiler chooses for instance_ and global, the call to Singleton::Instance may return an object that has not been constructed yet. This means that you cannot count on instance_ being initialized if other external objects are using it.

    I l@ve RuBoard Previous Section Next Section