[英]C++ singleton template class
在最近的一個項目中,我不得不創建一個Singleton類,經過大量的Google搜索后,我想出了這個模板類定義。 我們的想法是從這個模板類派生出來,並使派生類的構造函數受保護/私有。 它似乎運作良好,但我只在一個項目中使用它一個類,所以我希望你們中的一些人可以指出我是否在實現中犯了錯誤。 這里是:
/**
* @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;
我特別擔心頭文件中的靜態成員初始化以及當從SingleDynamic派生的類的頭文件包含在多個文件中時是否會導致多個定義錯誤。 我已經試過了,它似乎工作,但我無法弄清楚為什么它的工作:)。
在此先感謝,Ashish。
編輯:使用基於策略的設計修改實現,如接受的解決方案中所建議的那樣。
/**
* 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;
必須在頭文件中初始化模板類的靜態成員,並且最近的C ++編譯器及其鏈接器必須正確處理。
但你是對的,一些非常古老的編譯器有這個問題。
在這些情況下,對於使用單例模板的每種類型,在任意編譯單元中初始化靜態成員一次是一種解決方法。
gcc文檔具有相同的詳細信息: http : //gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html 。
我記得一個嵌入式項目(不久前),舊的編譯器仍然在使用,而且一個靜默地創建了模板的靜態成員的多個實例。 當它存在一個單身人士時,顯然是一個非常糟糕的主意....
更糟糕的是,庫中唯一使用的Singleton(第三方框架)是一些通常以相同方式初始化的Configuration對象,因此只有在運行時更改配置時才會出現bug。 花了幾天的時間來跟蹤這個錯誤,直到我們最終在反匯編中看到在不同的內存區域訪問“相同”的成員。
不需要那么復雜。
使用單例模式的簡單C ++記錄器
這里並發相關代碼的正確性很難評估。 在我看來,實施工作試圖有點過於聰明。
OTOH,所有與並發相關的代碼基本上都有它后面的存根,什么都不做。 如果在非線程環境中使用它,我認為應該沒問題。
但是,我也認為你的擔心是有根據的。 靜態成員的那些外部定義似乎違反了一個定義規則 。
就個人而言,我認為應該重寫此模板以將並發內容作為模板本身的策略參數,並要求派生類在適當的.cpp文件中聲明自己的pInstance
版本。
其他人建議在靜態局部變量的初始化方面依賴編譯器特定的行為。 我不認為這是一個可怕的建議,但是當你不能依賴編譯器做正確的事情時,有一個選項可能會很好。
目前,在C ++的多線程環境中懶惰地創建Singleton是不可能的。
一些大師(其中包括Herb Sutter)已經承認標准的當前狀態並不能保證任何東西。 有各種編譯器的黑客,而boost為這個目的提供了once
設施,但它是編譯器特定指令的雜亂集合......它不是標准的C ++(線程不知道)。
當前工作的唯一解決方案(根據標准)是在啟動多個線程之前初始化Singleton,或者在保證只有一個線程將訪問它的過程的一部分中初始化Singleton。
C ++ 0x將線程帶入標准,並且特別保證即使在存在多個線程的情況下也只會創建一次本地靜態變量(在多個同時調用的情況下,所有阻塞直到創建結束)。 因此以下方法:
static MyType& Instance() { static Instance MExemplar; return MExemplar; }
工作,在這種情況下,根本不需要單獨的模板類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.