C++ singleton classe de modèle

Dans un projet récent, j'ai dû créer une classe Singleton et après beaucoup de creuser autour sur Google j'ai trouvé ce modèle de définition de la classe. L'idée est de dériver de cette classe de modèle et de faire de la dérivée de la classe de constructeur protected /private. Il semble bien fonctionner mais je ne l'ai utilisé avec une seule classe dans un projet, j'ai donc été en espérant que certains d'entre vous pourraient rappeler si j'ai fait des erreurs dans la mise en œuvre. Ici, il est:

/**
* @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;

Je suis particulièrement préoccupé par le membre statique initialisations dans le fichier d'en-tête et si cela va causer de multiples erreurs de définition lorsque le fichier d'en-tête de la classe est dérivée de la SingleDynamic est inclus dans plusieurs fichiers. J'ai déjà essayé ça et ça semble marcher, mais je ne peux pas comprendre pourquoi sa fonctionne :).

Merci d'avance,
Ashish.

EDIT: Modifié la mise en œuvre d'une politique de conception comme le suggère la solution retenue.

/**
* 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;
Tu fais pas besoin d'un singleton. Ne pas utiliser un singleton. Vous voulez un mondial, il faut donc utiliser un mondial.
Je suis d'accord avec GMan à ne pas utiliser singeltons. Je suis en désaccord qu'ils sont les mêmes que globals (initialisation). Et je déteste utiliser des pointeurs comme il y a la représentation que ce ne sont pas supprimés automatiquement (utilisation d'une fonction statique de la variable à l'intérieur de get_instance() (garder les verrous) de cette façon, le singleton sera supprimé correctement). PS. Vous avez besoin de déplacer flag_ = true; dans les serrures, sinon vous risquez d'avoir plusieurs threads création de l'instance.
Eh bien, vous pouvez voler le creative solutions de création de singletons utiliser pour faire un beau mondial de l'utilitaire de bibliothèque. C'est surtout l'restreint instance de la merde c'est abrutissant et inutile. Je suis envisagent de soumettre une bibliothèque globale de coup de pouce, car il en manque un.
Il n'est pas question que flag_ = true; est à l'extérieur de l'exclusion mutuelle verrous; un seul thread peut aller au-delà de la (*SingletonDynamic< T >::pfnLockMutex)(); et ce thread va modifier la pInstance_ variable donc ce n'est pas NULL. Cela permettra d'éviter un thread en attente à la serrure de la création d'une nouvelle instance.
Vous êtes correct. Oups.

OriginalL'auteur Praetorian | 2010-08-09