最近碰到一个同事讨论全局访问的对象,想起来这个模式,比起这个模式更有意思的是这个模式各种实现中学习到的c++知识。
记录下我曾经学习到的单件模式,和曾经实现的版本。我给他提供的实现是meyer版本:
template<typename Type>
class Singleton
{
public:
static Type& GetInstance()
{
static Type value;
return value;
}
};
template<typename Type>
class Singleton;
class UserType
{
UserType()
{
}
~UserType()
{
}
friend class Singleton<UserType>;
public:
};
记得设计模式中没有考虑多线程,我提供一个类似的c++实现:
template<typename Type>
class Singleton
{
public:
static Type& GetInstance()
{
if(_instance==nullptr){
_instance=new Type;
}
return *_instance;
}
private:
static Type* _instance;
};
template<typename Type>
Type* Singleton<Type>::_instance=nullptr;
在这个基础上增加多线程锁,就是所谓的双重检测版本
#include <mutex>
template<typename Type>
class Singleton
{
public:
static Type& GetInstance()
{
if(_instance==nullptr){
std::lock_guard<std::mutex> lock(_mutex);
if(_instance==nullptr){
_instance=new Type;
}
}
return *_instance;
}
private:
static Type* _instance;
static std::mutex _mutex;
};
template<typename Type>
Type* Singleton<Type>::_instance=nullptr;
template<typename Type>
std::mutex Singleton<Type>::_mutex;
再后来,又是meyer发了篇论文说由于乱序,双重检测也不保险。就有了开始的meyer版本。不得不说的loki的实现,这个可以看书,也可以直接看源码,实现太繁琐,不贴代码了。上面的实现new出来后没有delete,loki中使用atexit函数,在程序exit中调用来释放内存。为了解决乱序的问题,使用atomic,有了如下实现,主要是memory battery解决了乱序的bug:
#include <atomic>
#include <mutex>
class spinlock_mutex
{
std::atomic_flag flag=ATOMIC_FLAG_INIT;
public:
void lock()
{
while (!flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
template<typename Type>
class Singleton
{
public:
static Type& GetInstance()
{
if(_instance==nullptr){
std::lock_guard<spinlock_mutex> lock(_mutex);
if(_instance==nullptr){
_instance=new Type;
}
}
return *_instance;
}
private:
static Type* _instance;
static spinlock_mutex _mutex;
};
template<typename Type>
Type* Singleton<Type>::_instance=nullptr;
template<typename Type>
spinlock_mutex Singleton<Type>::_mutex;
在上面的基础上,我给singleton增加ReleaseInstance函数,用来在程序退出时,手动释放对象,控制释放顺序,辅助类helper的存在是为了没有主动调用ReleaseInstance时,自动释放对象。
#include <atomic>
#include <mutex>
class spinlock_mutex
{
std::atomic_flag flag=ATOMIC_FLAG_INIT;
public:
void lock()
{
while (!flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
template<typename Type>
class Singleton
{
class Helper
{
public:
~Helper()
{
ReleaseInstan();
}
};
public:
static Type& GetInstance()
{
static Helper helper;
if(_instance==nullptr){
std::lock_guard<spinlock_mutex> lock(_mutex);
if(_instance==nullptr){
_instance=new Type;
}
}
return *_instance;
}
static void ReleaseInstan()
{
if(_instance){
delete _instance;
_instance=nullptr;
}
}
private:
static Type* _instance;
static spinlock_mutex _mutex;
};
template<typename Type>
Type* Singleton<Type>::_instance=nullptr;
template<typename Type>
spinlock_mutex Singleton<Type>::_mutex;
还有使用call_once
#include <mutex>
template<typename Type>
class Singleton
{
class Deleter
{
public:
~Deleter()
{
if(_instance){
delete _instance;
_instance=nullptr;
}
}
};
public:
static Type& GetInstance()
{
static Deleter deleter;
std::call_once(_flag,[](){
_instance=new Type;
});
return *_instance;
}
private:
static Type* _instance;
static std::once_flag _flag;
};
template<typename Type>
Type* Singleton<Type>::_instance=nullptr;
template<typename Type>
std::once_flag Singleton<Type>::_flag;
单件模式不喜欢,本身代码也比较简单,但是各种不同的单件实现中有不同的折中,有一些好玩的东西在里面。