C++ singleton-template-Klasse

In einem kürzlichen Projekt hatte ich das erstellen einer Singleton-Klasse und nach viel Graben, um auf Google kam ich mit dieser template-Klasse definiert. Die Idee ist die Ableitung von dieser template-Klasse und machen der abgeleiteten Klasse der Konstruktor protected /private. Es scheint gut zu funktionieren, aber ich habe nur verwendet es mit einer einzigen Klasse in einem Projekt, so hoffte ich, einige von Euch könnten darauf hinweisen, wenn ich Fehler gemacht habe bei der Umsetzung. Hier ist es:

/**
 * @brief 
 *    Singleton design pattern implementation using a dynamically allocated singleton instance.
 *
 * The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
 * design pattern and require lazy initialization of the singleton object. The default 
 * implementation is not thread-safe, however, the derived classes can make it so by reinitializing
 * the function pointers SingletonDynamic<T>::pfnLockMutex, SingletonDynamic<T>::pfnUnlockMutex
 * and SingletonDynamic<T>::pfnMemoryBarrier. The member function pointers are initialized by 
 * default to point to placeholder functions that do not perform any function. The derived class
 * must provide alternate implementations for SingletonDynamic<T>::lock_mutex(),
 * SingletonDynamic<T>::unlock_mutex() and SingletonDynamic<T>::memory_barrier() respectively
 * and reinitialize the respective function pointer members to these alternate implementations.
 *
 * @tparam T
 *    The type name of the derived (singleton) class
 *
 * @note The derived class must have a no-throw default constructor and a no-throw destructor.
 * @note The derived class must list this class as a friend, since, by necessity, the derived class'
 *       constructors must be protected /private.
 */
template< typename T >
class SingletonDynamic
{
public:
  /**
   * Factory function for vending mutable references to the sole instance of the singleton object.
   *
   * @return A mutable reference to the one and only instance of the singleton object.
   */
  static T &instance()
  {
    return *SingletonDynamic< T >::get_instance();
  }


  /**
   * Factory function for vending constant references to the sole instance of the singleton object.
   *
   * @return A constant reference to the one and only instance of the singleton object.
   */
  static const T &const_instance()
  {
    return *SingletonDynamic< T >::get_instance();
  }

protected:
  /** Default constructor */
  SingletonDynamic() {}

  /** Destructor */
  virtual ~SingletonDynamic() 
  {
    delete SingletonDynamic< T >::pInstance_;
  }

  /** Defines an alias for a function pointer type for executing functions related to thread-safety */
  typedef void(*coherence_callback_type)();

  /** 
   * Pointer to a function that will lock a mutex denying access to threads other that the current 
   * 
   * @note The function must have the signature void foo()
   * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
   *       default value must be left unchanged if this functionality is not desired.
   */
  static coherence_callback_type  pfnLockMutex;

  /** 
   * Pointer to a function that will unlock a mutex allowing access to other threads 
   * 
   * @note The function must have the signature void foo()
   * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
   *       default value must be left unchanged if this functionality is not desired.
   */
  static coherence_callback_type  pfnUnlockMutex;

  /** 
   * Pointer to a function that executes a memory barrier instruction that prevents the compiler
   * from reordering reads and writes across this boundary.
   * 
   * @note The function must have the signature void foo()
   * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
   *       default value must be left unchanged if this functionality is not desired.
   */
  static coherence_callback_type  pfnMemoryBarrier;

private:
  /** The sole instance of the singleton object */
  static T *pInstance_;

  /** Flag indicating whether the singleton object has been created */
  static volatile bool flag_;

  /** Private copy constructor to prevent copy construction */
  SingletonDynamic( SingletonDynamic const & );

  /** Private operator to prevent assignment */
  SingletonDynamic &operator=( SingletonDynamic const & );


  /** 
   * Fetches a pointer to the singleton object, after creating it if necessary
   *
   * @return A pointer to the one and only instance of the singleton object.
   */
  static T *get_instance()
  {
    if( SingletonDynamic< T >::flag_ == false ) {
      /* acquire lock */
      (*SingletonDynamic< T >::pfnLockMutex)();

      if( SingletonDynamic< T >::pInstance_ == NULL ) {
        pInstance_ = new T();
      }

      /* release lock */
      (*SingletonDynamic< T >::pfnUnlockMutex)();

      /* enforce all prior I/O to be completed */
      (*SingletonDynamic< T >::pfnMemoryBarrier)();

      SingletonDynamic< T >::flag_ = true;

      return SingletonDynamic< T >::pInstance_;
    } else {
      /* enforce all prior I/O to be completed */
      (*SingletonDynamic< T >::pfnMemoryBarrier)();

      return SingletonDynamic< T >::pInstance_;
    }
  }


  /**
   * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
   * default implementation does not perform any function, the derived class must provide an 
   * implementation if this functionality is desired.
   */
  inline static void lock_mutex()
  {
    /* default implementation does nothing */
    return;
  }


  /**
   * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
   * default implementation does not perform any function, the derived class must provide an 
   * implementation if this functionality is desired.
   */
  inline static void unlock_mutex()
  {
    /* default implementation does nothing */
    return;
  }


  /**
   * Placeholder function for executing a memory barrier instruction, thereby preventing the 
   * compiler from reordering read and writes across this boundary. This default implementation does 
   * not perform any function, the derived class must provide an implementation if this 
   * functionality is desired.
   */
  inline static void memory_barrier()
  {
    /* default implementation does nothing */
    return;
  }
};

/* Initialize the singleton instance pointer */
template< typename T >
T *SingletonDynamic<T>::pInstance_        = NULL;

/* Initialize the singleton flag */
template< typename T >
volatile bool SingletonDynamic<T>::flag_  = false;

/* Initialize the function pointer that locks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnLockMutex  
                                                              = &SingletonDynamic<T>::lock_mutex;

/* Initialize the function pointer that unlocks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnUnlockMutex  
                                                              = &SingletonDynamic<T>::unlock_mutex;

/* Initialize the function pointer that executes the memory barrier instruction */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnMemoryBarrier
                                                              = &SingletonDynamic<T>::memory_barrier;

Ich bin besonders besorgt über die statischen member-Initialisierungen in der header-Datei und ob das wird dazu führen, dass mehrere definition-Fehler, wenn die header-Datei der abgeleiteten Klasse aus der SingleDynamic enthalten ist, in mehreren Dateien. Habe ich schon ausprobiert und es scheint zu funktionieren, aber ich kann nicht herausfinden, warum seine arbeiten :).

Vielen Dank im Voraus,
Ashish.

EDIT: Geändert Implementierung die Verwendung einer policy-based design wie bereits in der akzeptierten Lösung.

/**
 * This is the default ConcurrencyPolicy implementation for the SingletonDynamic class. This 
 * implementation does not provide thread-safety and is merely a placeholder. Classes deriving from
 * SingletonDynamic must provide alternate ConcurrencyPolicy implementations if thread-safety is
 * desired.
 */
struct DefaultSingletonConcurrencyPolicy
{
  /**
   * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
   * default implementation does not perform any function, the derived class must provide an 
   * alternate implementation if this functionality is desired.
   */
  static void lock_mutex() 
  { 
    /* default implementation does nothing */
    return;
  }

  /**
   * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
   * default implementation does not perform any function, the derived class must provide an 
   * alternate implementation if this functionality is desired.
   */
  static void unlock_mutex()
  {
    /* default implementation does nothing */
    return;
  }

  /**
   * Placeholder function for executing a memory barrier instruction, thereby preventing the 
   * compiler from reordering read and writes across this boundary. This default implementation does 
   * not perform any function, the derived class must provide an alternate implementation if this 
   * functionality is desired.
   */
  static void memory_barrier()
  {
    /* default implementation does nothing */
    return;
  }
};


/**
 * @brief 
 *    Singleton design pattern implementation using a dynamically allocated singleton instance.
 *
 * The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
 * design pattern and that dynamic allocation of the singleton object. The default implementation 
 * is not thread-safe; however, the class uses a policy-based design pattern that allows the derived 
 * classes to achieve threaad-safety by providing an alternate implementation of the 
 * ConcurrencyPolicy.
 *
 * @tparam T
 *    The type name of the derived (singleton) class
 * @tparam ConcurrencyPolicy
 *    The policy implementation for providing thread-safety
 *
 * @note The derived class must have a no-throw default constructor and a no-throw destructor.
 * @note The derived class must list this class as a friend, since, by necessity, the derived class'
 *       constructors must be protected /private.
 */
template< typename T, typename ConcurrencyPolicy = DefaultSingletonConcurrencyPolicy >
class SingletonDynamic : public ConcurrencyPolicy
{
public:
  /**
   * Factory function for vending mutable references to the sole instance of the singleton object.
   *
   * @return A mutable reference to the one and only instance of the singleton object.
   */
  static T &instance()
  {
    return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
  }


  /**
   * Factory function for vending constant references to the sole instance of the singleton object.
   *
   * @return A constant reference to the one and only instance of the singleton object.
   */
  static const T &const_instance()
  {
    return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
  }

protected:
  /** Default constructor */
  SingletonDynamic() {}

  /** Destructor */
  virtual ~SingletonDynamic() 
  {
    delete SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
  }

private:
  /** The sole instance of the singleton object */
  static T *pInstance_;

  /** Flag indicating whether the singleton object has been created */
  static volatile bool flag_;

  /** Private copy constructor to prevent copy construction */
  SingletonDynamic( SingletonDynamic const & );

  /** Private operator to prevent assignment */
  SingletonDynamic &operator=( SingletonDynamic const & );


  /** 
   * Fetches a pointer to the singleton object, after creating it if necessary
   *
   * @return A pointer to the one and only instance of the singleton object.
   */
  static T *get_instance()
  {
    if( SingletonDynamic< T, ConcurrencyPolicy >::flag_ == false ) {
      /* acquire lock */
      ConcurrencyPolicy::lock_mutex();

      /* create the singleton object if this is the first time */
      if( SingletonDynamic< T, ConcurrencyPolicy >::pInstance_ == NULL ) {
        pInstance_ = new T();
      }

      /* release lock */
      ConcurrencyPolicy::unlock_mutex();

      /* enforce all prior I/O to be completed */
      ConcurrencyPolicy::memory_barrier();

      /* set flag to indicate singleton has been created */
      SingletonDynamic< T, ConcurrencyPolicy >::flag_ = true;

      return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
    } else {
      /* enforce all prior I/O to be completed */
      ConcurrencyPolicy::memory_barrier();

      return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
    }
  }
};

/* Initialize the singleton instance pointer */
template< typename T, typename ConcurrencyPolicy >
T *SingletonDynamic< T , ConcurrencyPolicy >::pInstance_        = NULL;

/* Initialize the singleton flag */
template< typename T, typename ConcurrencyPolicy >
volatile bool SingletonDynamic< T , ConcurrencyPolicy >::flag_  = false;
Sie nicht brauchen Sie ein singleton. nicht mit einem singleton. Sie wollen eine Globale, so verwenden Sie eine Globale.
Ich Stimme mit GMan in nicht verwenden singeltons. Ich bin nicht einverstanden, dass Sie das gleiche wie globals (lazy initialization). Und ich hasse, mit Zeigern, wie es die Darstellung, da diese nicht automatisch gelöscht werden (verwenden Sie eine statische variable, Funktion innerhalb get_instance() (sperren), die Art und Weise der singleton richtig gelöscht werden). PS. Sie müssen sich zu bewegen flag_ = true; im inneren der Schlösser sonst könnte Euch mehrere threads erstellen der Instanz.
Auch Sie können zu stehlen den kreativen Schöpfung Lösungen singletons verwenden, um eine schöne Globale utility-Bibliothek. Es ist vor allem die eingeschränkte Instanz Scheiße, das ist Verschleierung und unnötig. Ich überlege mir die Einreichung einer globalen Bibliothek zu Steigern, da es fehlt.
Es spielt keine Rolle, dass flag_ = true; außerhalb des mutex-locks, nur ein thread über den (*SingletonDynamic< T >::pfnLockMutex)(); und dieser thread ändern Sie die pInstance_ variable ist also nicht NULL. Dies wird verhindern, dass ein thread wartet auf die Sperre von dem erstellen einer neuen Instanz.
Sind Sie richtig. Hoppla.

InformationsquelleAutor Praetorian | 2010-08-09

Schreibe einen Kommentar