单例模式:
只能有一个实例,有懒汉和饿汉区分,实现核心思想:
1.构造函数私有化
2.使用静态函数作为接口来获取类对象
1、懒汉模式:
由调用者实例,多线程情况下会存在线程安全问题,需要加互斥锁进行防护
2、饿汉模式:
没有人为实例对象时,就把对象给实例好,当需要使用时只要通过接口函数直接获取对象
注意:
饿汉模式new的对象,需要人为处理,否则会导致内存泄漏,可以使用智能指针进行管理,或者写个静态的类中类,让类中类进行析构释放
代码示例:
饿汉模式
//饿汉模式,没有线程安全问题,无需加锁
class singleHungry
{
private:
int dataA;
int dataB;
private:
//构造函数私有化
singleHungry(int a, int b):dataA(a),dataB(b){}
//禁用默认拷贝构造、赋值运算符函数
singleHungry(const singleHungry& s) = delete;
singleHungry& operator= (const singleHungry& s) = delete;
public:
~singleHungry(){}
static singleHungry s_instacne;
static singleHungry& getInstacne();
//业务逻辑
int getAddResult()
{
int result = dataA + dataB;
cout<<"add result = "<<result<<endl;
return result;
}
};
//静态变量需要在类外进行初始化,由于对象是静态变量,存储在静态存储区,无需人为回收,进程结束自动回收内存
singleHungry singleHungry::s_instacne(1,2);
singleHungry& singleHungry::getInstacne()
{
return s_instacne;
}
懒汉模式
//懒汉模式,存在线程安全问题,需要加锁
class singleLazy
{
private:
int dataA;
int dataB;
private:
//构造函数私有化
singleLazy(int a, int b):dataA(a),dataB(b){}
//禁用默认拷贝构造、赋值运算符函数
singleLazy(const singleLazy& s) = delete;
singleLazy& operator= (const singleLazy& s) = delete;
public:
~singleLazy(){}
//互斥锁解决线程安全问题
static mutex s_mute;
//智能指针管理对象,解决内存泄漏问题
static shared_ptr<singleLazy> s_instance;
static shared_ptr<singleLazy> createInstance();
//业务逻辑
int getSubResult()
{
int result = dataA - dataB;
cout<<"sub result = "<<result<<endl;
return result;
}
};
//静态变量需要在类外进行初始化
mutex singleLazy::s_mute;
shared_ptr<singleLazy> singleLazy::s_instance = nullptr;
shared_ptr<singleLazy> singleLazy::createInstance()
{
if (nullptr == s_instance) //未构造
{
s_mute.lock();
cout<<"first structure"<<endl;
s_instance = shared_ptr<singleLazy>(new singleLazy(3,4));
s_mute.unlock();
}
else
{
cout<<"has structured"<<endl;
}
return s_instance;
}
完整代码如下:
#include <iostream>
#include <mutex>
#include <memory>
using namespace std;//饿汉模式,没有线程安全问题,无需加锁
class singleHungry
{
private:
int dataA;
int dataB;
private:
//构造函数私有化
singleHungry(int a, int b):dataA(a),dataB(b){}//禁用默认拷贝构造、赋值运算符函数
singleHungry(const singleHungry& s) = delete;
singleHungry& operator= (const singleHungry& s) = delete;
public:
~singleHungry(){}
static singleHungry s_instacne;
static singleHungry& getInstacne();//业务逻辑
int getAddResult()
{
int result = dataA + dataB;
cout<<"add result = "<<result<<endl;
return result;
}
};//静态变量需要在类外进行初始化,由于对象是静态变量,存储在静态存储区,无需人为回收,进程结束自动回收内存
singleHungry singleHungry::s_instacne(1,2);
singleHungry& singleHungry::getInstacne()
{
return s_instacne;
}
//懒汉模式,存在线程安全问题,需要加锁
class singleLazy
{
private:
int dataA;
int dataB;
private:
//构造函数私有化
singleLazy(int a, int b):dataA(a),dataB(b){}
//禁用默认拷贝构造、赋值运算符函数
singleLazy(const singleLazy& s) = delete;
singleLazy& operator= (const singleLazy& s) = delete;
public:
~singleLazy(){}
//互斥锁解决线程安全问题
static mutex s_mute;
//智能指针管理对象,解决内存泄漏问题
static shared_ptr<singleLazy> s_instance;
static shared_ptr<singleLazy> createInstance();//业务逻辑
int getSubResult()
{
int result = dataA - dataB;
cout<<"sub result = "<<result<<endl;
return result;
}
};//静态变量需要在类外进行初始化
mutex singleLazy::s_mute;
shared_ptr<singleLazy> singleLazy::s_instance = nullptr;
shared_ptr<singleLazy> singleLazy::createInstance()
{
if (nullptr == s_instance) //未构造
{
s_mute.lock();
cout<<"first structure"<<endl;
s_instance = shared_ptr<singleLazy>(new singleLazy(3,4));
s_mute.unlock();
}
else
{
cout<<"has structured"<<endl;
}return s_instance;
}
//客户端
int main()
{
//饿汉模式
singleHungry::getInstacne().getAddResult();//懒汉模式
singleLazy::createInstance()->getSubResult();
singleLazy::createInstance()->getSubResult();
return 0;
}
【采用单例模式动机、原因】
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。