什么是单例模式:
- 一个类只能实例化一个对象叫做单例模式
- 单例模式也称单子模式、单件模式,通过单例模式可以保证系统中只有一个类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享
单例模式的优点:
- 在内存中只有一个对象,节省内存空间
- 避免频繁的创建销毁对象,可以提高性能
- 避免对共享资源的多重占用
- 可以全局访问
单例模式的适用场景:
- 需要频繁实例化然后销毁的对象
- 创建对象耗时过多或者耗资源过多,但又经常用到的对象
- 有状态的工具类对象
- 频繁访问数据库或文件的对象
- 以及其他要求只有一个对象的场景
实现方式:
- 将构造、拷贝、赋值方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。
- 定义一个静态方法返回这个唯一对象。
单例模式分为两种:饿汉/懒汉
饿汉模式:
在加载类时就已经将对象创建完毕。饿汉式的特点是一开始就加载了,因为一开始就创建了实例,所以每次用到的时候直接调用static的接口就好了。饿汉模式是线程安全的,也是因为其一开始就创建好的原因。
class Singleton
{
private:
Singleton(); //构造、拷贝、赋值方法私有化
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton* Get()
{
static Singleton _s;//l类对象
return &_s;
}
};
懒汉模式:
第一次用到类的对象的时候才会实例化。
class CSingleton
{
private:
CSingleton();
CSingleton(const CSingleton&);
CSingleton& operator = (const CSingleton&);
public:
static CSingleton* Get()
{
static CSingleton* _cs;
if (_cs == nullptr) //用到的时候才会实例化对象
_cs = new CSingleton();
return _cs;
}
};
懒汉模式在单线程的情况下没有问题,但是正是因为用到的时候才会创建对象,这也就成为了懒汉模式的缺点,若是多个线程同时首次进入Get()方法中,因为_cs == nullptr ,所以多个线程就会同时创建出_cs,就违背了单例模式的基本要求。
线程安全懒汉模式:
#include <mutex>
std::mutex _mut;
class CSingleton
{
private:
CSingleton();
CSingleton(const CSingleton&);
CSingleton& operator = (const CSingleton&);
public:
static CSingleton* Get()
{
static CSingleton* _cs;
if (_cs == nullptr)
{
//上锁保证访问的互斥性,用到了unique_lock,因为可以自动的加/解锁,但是会消耗更多的资源
std::unique_lock<std::mutex> lock(_mut);
//判断两次,加锁之后再判断,因为多个线程有可能同时进入这一步
if (_cs == nullptr)
{
_cs = new CSingleton();
}
}
return _cs;
}
};
两者对比:
- 饿汉模式:加载类慢,获取对象快,线程安全
- 懒汉模式:加载类快,获取对象慢,线程不安全