单例模式应该说是最简单的设计模式了。在此分享一个线程安全的单例模板类。
template <typename Type>
class CSingleton
{
public:
static Type* GetInstance()
{
// kBeingCreatedMarker用来表示单例实例正在创建过程中。
// 此处初始化为1是因为操作系统不会分配地址为1的指针。
static const volatile intptr_t kBeingCreatedMarker = 1;
// 如果m_pInstance不为空且不是正在创建,则返回m_pInstance
if (m_pInstance != NULL && m_pInstance != kBeingCreatedMarker) {
return reinterpret_cast<Type*>(m_pInstance);
}
// 使用InterlockedCompareExchange函数保证原子操作
// 函数判断m_pInstance是否等于NULL,如果是则将m_pInstance赋值为kBeingCreatedMarker
// 函数返回值为m_pInstance的初始值,通过判断返回值是否等于NULL得知是否可以进行实例化
if (InterlockedCompareExchange(
reinterpret_cast<volatile LONG*>(&m_pInstance),
static_cast<LONG>(kBeingCreatedMarker),
static_cast<LONG>(NULL)) == NULL) {
static Type newval;
m_pInstance = reinterpret_cast<intptr_t>(&newval);
return &newval;
}
// 如果m_pInstance是kBeingCreatedMarker,即表示正在创建中
// SwitchToThread让出剩余的时间片等待创建过程完成
while (m_pInstance == kBeingCreatedMarker)
{
SwitchToThread();
}
// 到达此处表明创建过程已经完成了
return reinterpret_cast<Type*>(m_pInstance);
}
Type& operator*()
{
return *GetInstance();
}
Type* operator->()
{
return GetInstance();
}
private:
static volatile intptr_t m_pInstance;
};
template <typename Type>
volatile intptr_t CSingleton<Type>::m_pInstance = NULL;
假设我们有个CCmdManager类
class CCmdManager
{
public:
CCmdManager()
{
std::cout << "Hello, I am the only one!";
};
};
使用方法很简单,如下:
int main()
{
CCmdManager *pMgr = CSingleton<CCmdManager>::GetInstance();
CCmdManager &mgr = *(CSingleton<CCmdManager>::GetInstance());
}
如果我们想要完全限制CCmdManager不被实例化第二次,我们可以这么做
class CCmdManager
{
private:
CCmdManager()
{
std::cout << "Hello, I am the only one!";
};
friend class CSingleton<CCmdManager>;
};
通过将构造函数设置为private,且仅对class CSingleton<CCmdManager>开放,就可以保证用户只能使用CCmdManager *pMgr = CSingleton<CCmdManager>::GetInstance()这种方式调用了。
顺便说一下,这个代码的一些局限性
1. 因为使用了InterlockedCompareExchange这个函数,所以只能在Windows下使用,但是不可否定的是这个函数的效率极高,完成比较并交换只要一条指令。
2. CSingleton在实例化对象时,只支持默认构造函数。一般情况下,这个也是可以接受的。