Monday, June 1, 2015

Implement a Singleton Base class

Despite the classic version of Singleton written by Scott Meyers, I was trying to implement a base singleton class for inheritance.

For comparison, I put the classic version here:

(1) static reference version, lazy initialization
class S{
public:
    static S& GetInstance(){
        static S instance;
        return instance;
    }

private:
    S() {};
    S(S const&);
    void operator=(S const&);

    // c++11
    S(S const&) = delete;
    void operator=(S const&) = delete;

};
Note: It is worth to note that this work well in multi-thread context as in C++11(using g++4.8.1 compiler), it is guaranteed that only one thread will be able to access the static variable at one time.
If for window, sync mechanism is required, like ::EnterCriticalSection();


(2) static pointer version
class S{
public:
    static S* GetInstance(){
        if(uptr.get() == 0){
            uptr.reset(new S());
        }
        return uptr;
    }
private:
    static std::unique_ptr<S> uptr;
 
    S(){};
    S(S const&) = delete;
    void operator=(S const&) = delete;
}


(3) Lock version:

static S* GetInstance(){
     if(!pInstance){
           std::lock_guard<mutex>lock(mutex);
           if(!pInstance){
                  S *temp = new S();
                  pInstance = temp;
           }
     }
     return pInstance;
}
 



Before I did my work, I was aware of several things need be taken serious considerations:
one is that although it is guaranteed that static variables can only be access to by only one thread under c++11 and initialized when the declaration is encountered, the compiler may still modify its state(by adding some additional instructions before its initialization), the other one needs be carefully taken is when you refer to the singleton in the constructor of the singleton class, infinite loop may occur.

For practical usage, it is ideal if I can implement a base singleton class for inherit so that the class users wouldn't need to be care of too much detail.

#include <memory>

template <class T>
class Singleton
{
public :
  static T& GetInstance()
  {
    static typename T::singleton_pointer_type s_singleton(T::createInstance()) ;

    return getReference(s_singleton) ;
  }

private :
  typedef std::unique_ptr<T> singleton_pointer_type ;

  static T *createInstance() {
      return new T() ;
  }

  static T &getReference(const singleton_pointer_type &ptr) {
      return *ptr ;
  }

protected :
  Singleton() {}

private :
  Singleton(const Singleton &) = delete;
  Singleton& operator=(const Singleton &) = delete;
  Singleton(Singleton &&) = delete;
  Singleton& operator=(Singleton &&) = delete;
};

Because the initialization is delayed until the function is called, static local variables' value does not depend on the initialization order of them, it also be referred to simultaneously by a plurality In C ++ 11 threads are initialized only once , it guarantees that all of the call is locked until the initialization is complete.

In addition, by a static pointer rather than std :: unique_ptr in a local variable T <T>, the destructor at the end of the program is called, benefits is that, you can customize how to build an instance in the actual class that inherits from Singleton the way you specified. finally, it is suggested to remove the copy and move constructor and assignment operator.


How to use this base singleton class?

class Me: public Singleton<Me>
{
private :
  friend class Singleton<Me>;
  Me() { }

  // If you want to build an instance not using default constructor, override the createInstance ()
  // Me(int) { }
  // static Me *createInstance(){
  //      return new Me(0);
  // }
};





No comments:

Post a Comment