1.单例模式
1.概念
- 单例(Singleton)模式,是一种常用的软件设计模式
- 在它的核心结构中只包含一个被称为单例的特殊类
- 通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例
- 即一个类只有一个对象实例
2.使用场景
- 语义上只需要一个
- 该对象内部存在大量的空间,保存了大量的数据,如果允许该对象存在多份,或者允许发生各种拷贝,内存中存在冗余数据
3.单例模式有两种实现模式
- **饿汉模式:**吃完饭,立刻洗碗,因为下一顿吃的时候可以立刻拿着碗就能吃饭
- **懒汉模式:**吃完饭,先把碗放下,然后下一顿饭用到这个碗的时候,再洗碗
- 懒汉模式最核心的思想就是**“延时加载”**
2.饿汉模式
- 该模式在类被加载时就会实例化一个对象
- 该模式能简单快速的创建一个单例对象,而且是线程安全的(只在类加载时才会初始化,以后都不会)
- **缺点:**不管需不需要,都会直接创建一个对象,会消耗一定的性能(但很小)
template <typename T>
class Singleton
{
private:
static Singleton<T> data;
public:
static Singleton<T> *GetInstance()
{
return &data;
}
};
3.懒汉模式
- 该模式只在需要对象时才会生成单例对象(比如调用GetInstance方法)
- 以下代码看上去没什么明显问题,但它不是线程安全的
- 假设当前有多个线程同时调用GetInstance(),由于当前还没有对象生成,那么就会由多个线程创建多个对象
template <typename T>
class Singleton
{
private:
static Singleton<T> *inst;
public:
static Singleton<T> *GetInstance()
{
if (inst == NULL)
{
inst = new Singleton<T>();
}
return inst;
}
};
- 以下为线程安全的懒汉模式
- 注意事项:
-
加锁解锁的位置
-
双重if判定,避免不必要的锁竞争
-
volatile关键字防止过度优化
-
必然只能有一个线程竞争锁成功,此时再次判断有没有对象被创建(就是inst指针)
-
如果没有就会new一个对象,如果有就会解锁,并返回已有的对象
-
总的来说,这样的形式使得多个线程调用GetInstance方法时,无论成功与否,都会有返回值
-
template <typename T>
class Singleton
{
private:
static Singleton<T> *inst;
static std::mutex lock;
public:
static T *GetInstance()
{
if (inst == NULL) // 双重判定空指针, 降低锁冲突的概率, 提高性能
{
lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次new
if (inst == NULL)
{
inst = new Singleton<T>();
}
lock.unlock();
}
return inst;
}
};
4.懒汉模式中,为何GetInstance要设置成静态?
- 正常来说非静态成员函数是可以通过对象访问到静态成员变量的,这个没有问题
- 主要是单例对象在实例化出对象前没有对象可以给你借由它访问到那个非静态成员函数,自然也就没办法访问到静态成员变量了
- 所以将GetInstance设置成静态成员函数,即可在没有对象时,通过类名::函数名直接调用