概述
什么是单例模式
在整个系统的运行过程中保证某个类的实例有且只有一个的实现方式叫做单例模式
单例模式的实现思路
如何来保证类的实例在整个系统中只有一个呢?让用户自己去创建唯一实例?这怎么可能,只要用户能创建第一个实例就能创建第二个实例。那怎么保证唯一实例呢?答案就是在单例类上做手脚,第一:不能让用户随便创建类的实例;第二:既然用户不能创建实例,那就得我们自己创建实例;第三:提供唯一的接口让用户获取我们创建的唯一实例
私有化构造函数
构造函数是用来初始化实例的函数,将构造函数设置为私有,这样就防止了用户自己创建实例的问题
私有化静态成员变量保存唯一实例
为了在创建唯一实例后下次使用时还能找到,添加一个类类型的指针成员变量,在创建后使用该对象那个存储;为了保证成员变量的唯一性,将变量声明为静态成员变量,为了让用户只能通过唯一接口去访问实例,将成员变量声明为私有成员
静态成员函数返回类的实例
该函数需要在没有类的实例的情况下就能被调用,所以声明为静态成员函数,只需要通过类名作用域就可以直接访问。函数的作用就是返回唯一的实例对象。
单例模式的实现方式
单例模式有两种实现方式,一种称为饿汉式,一种称为懒汉式。两者的本质区别在于:创建单例类实例的时机不同。饿汉式----在程序启动时就为静态成员初始化从而创建实例。懒汉式—在使用时通过Singleton ::instance()函数调用,才会创建类的实例,如果一直不使用则一直不会有实例被创建。下面是两种方式的代码实现
饿汉式
/-----以下为.h文件中内容
class Singleton
{
private:
Singleton(){};//私有化构造函数,防止用户自己创建类的实例
Singleton(Singleton &sig){};//私有化构造函数,防止用户自己创建类的实例
~Singleton(){};//私有化析构函数,提供统一的销毁接口
public:
static Singleton * instance(){//获取单例类实例的唯一接口
return _instance;
}
static void disInstance(){//销毁单例实例的唯一接口
if(nullptr != _instance)
{
delete _instance;
_instance = nullptr;
}
}
private:
static Singleton *_instance;//静态私有成员变量保证类有且只有一个实例
};
-----以下为在.cpp文件对静态成员变量初始化的程序
Singleton * Singleton::_instance = new Singleton();
懒汉式
/-----以下为.h文件中内容
class Singleton
{
private:
Singleton(){};//私有化构造函数,防止用户自己创建类的实例
Singleton(Singleton &sig){};//私有化构造函数,防止用户自己创建类的实例
~Singleton(){};//私有化析构函数,提供统一的销毁接口
public:
static Singleton * instance(){//获取单例类实例的唯一接口----与饿汉式的区别一
if(nullptr == _instance)//保证在使用的时候才会创建
{
_instance = new Singleton();
}
return _instance;
}
static void disInstance(){//销毁单例实例的唯一接口
if(nullptr != _instance)
{
delete _instance;
_instance = nullptr;
}
}
private:
static Singleton *_instance;//静态私有成员变量保证类有且只有一个实例
};
-----以下为在.cpp文件对静态成员变量初始化的程序
Singleton * Singleton::_instance = nullptr;///-----------------与饿汉式的区别二
懒汉式遇到多线程
现在思考一个问题,当在多线程的情况下懒汉式如上的写作方式真的可以保证程序中唯一的实例吗?
情况如下:线程一,线程二,线程三。。。线程N都可以调用Singleton ::instance()函数,当其中一个线程调用函数instance时假设是该懒汉单例第一次被调用,此时判断if(nullptr == _instance)为真,然后开始创建实例,在创建完成之前的时间内,如果有其它的任何一个线程同时也调用Singleton ::instance()函数,函数体执行if(nullptr == _instance)判断仍然为真,也开始创建类的实例。这下不完犊子了吗,至少目前就会创建2个单例类的实例了,而且第二次创建的实例会覆盖掉第一次创建的实例,还有内存泄露的问题,要是有多个线程同时调用Singleton ::instance()函数呢?想想就恐怖,不但没有保证为该类创建唯一的实例,还有可能造成内存泄漏。
针对上面的情况怎么办呢?
第一个版本:加锁
修改获取实例的函数
//在饿汉式单例类中添加互斥锁成员std::mutex mute;
static Singleton * instance(){
mute.lock();//在检测实例是否为空之前首先加锁,保证同时只有一个线程可以执行,当创建完成后解锁,其他线程此时获得锁,再判断的时候已经创建实例,则不会再创建,保证了多线程中的安全性
if(nullptr == _instance)
{
_instance = new Singleton();
}
mute.unlock();
return _instance;
}
加锁,如此甚好,保证了在多线程的情况下也可以只为单例类创建一个实例,但是是不是代价有点大?如果该单例类处在数据交互的关键点,每次对数据进行操作均需要加锁开锁,繁重,耗时,会严重拖慢系统的性能,如何处理?
第二版本:双重判断
代码如下:
static Singleton * instance(){
if(nullptr == _instance){//第一重判断,如果已经创建实例则不再执行后续加锁解锁的过程
mute.lock();//在检测实例是否为空之前首先加锁,保证同时只有一个线程可以执行
//当创建完成后解锁,其他线程此时获得锁,再判断的时候已经创建实例,
//则不会再创建,保证了多线程中的安全性
if(nullptr == _instance)//第二重判断,没有创建实例则执行创建实例的过程,否则不执行
{
_instance = new Singleton();
}
mute.unlock();
}
return _instance;
}
最后
以上就是热心纸飞机为你收集整理的C++ 单例的饿汉式和懒汉式及懒汉式遇到多线程的终极处理方式的全部内容,希望文章能够帮你解决C++ 单例的饿汉式和懒汉式及懒汉式遇到多线程的终极处理方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复