单件模式应该是设计模式中运用得最多的模式之一,对于全局性的复用性的对象我们通常会采用单件模式,正是因为单件这种频繁运用的看似简单的模式前段时间在我们的项目中却引发了一个致命性的崩溃bug,因此在这里总结一下,希望能给大家有所帮助。所谓单件模式就是在整个进程运行期间只需要实例化一次为所有运行线程公用的对象。正因为单件可以为多个线程共用所以我们必须要保证它的线程安全性。通常实现单件有两种方式
方式一:
`class Singnton
{
public:
static Singleton* GetInstance()
{
LOCK
if(null == s_instance)
{
s_instance = new Singleton;
}
return s_instance;
}
static void ReleaseInstance()
{
LOCK
delete s_instance;
s_instance = null;
}
private:
Singleton():s_instance(null){}
~Singleton(){}
static Singleton* s_instance;
}
方式二:
class Singnton
{
public:
static Singleton* GetInstance()
{
static Singleton s_instance;
return &s_instance;
}
private:
Singleton():s_instance(null){}
~Singleton(){}
static Singleton* s_instance;
}
还有第三种方式就是通过全局静态变量来实现,但是这种方式相比以上两种不具有lazy特性(延迟初始化),因此一般不推荐使用,
我们首先看第一种方式,通过在heap上实例化对象来实现,这种方式必须要在进程退出之前手动释放heap上分配的内存,看似要麻烦一些。第二种是通过声明一个局部静态变量在static-global区域上实例化一个对象,由于该变量实例化在静态全局区域,所以我们不需要关心其内存释放问题。因为静态全局区域是在程序退出之时由操作系统自己回收的。好的,看起来第二种方式显然更方便一些。我们不用管理麻烦的内存释放问题。但是有时第二种方式会导致很奇怪的bug,比如如果你在一个线程中使用该单件类,在进程退出时做一些清理操作,由于静态全局区域的不可控性,此时该进程的静态全局区域有可能已被操作系统释放清理该静态变量因此已被释放,但是你的其他线程中可能还在用该静态变量这样传出去的指针已经成了悬浮指针(野指针)会导致严重的内存错误!!!正是由于全局静态区域生命期的不可控性导致了这种奇怪的难以察觉的Bug.因此我们在实现单件类时通常采用第一种方式,即使麻烦些,但是我们能准确控制该变量内存的生命期(在确定已经处理完后调用releaseinstance释放该heap区域即可),这样就不会出现在程序退出时出现的奇怪的崩溃bug了(一般是多线程中才会出现)。