一、简介
常用场景:日志模块,数据库模块
单例模式指的是一个类无论创建多少次对象,永远只能得到该类型一个对象的实例。
实现条件:
- 构造函数私有化,禁止用户任意创建对象
- 定义该类唯一的实例对象
- 通过一个static静态成员方法返回唯一的对象实例
- 禁用拷贝构造和拷贝复制运算符
二、饿汉式单例模式
#include<iostream>
using namespace std;
class Singleton
{
public:static Singleton* getInstance() // 获取类的唯一实例对象的接口方法{return &single;}
private:static Singleton single; // 定义一个唯一的实例对象Singleton() { cout << "Singleton()" << endl; } // 构造私有化~Singleton() { cout << "~Singleton()" << endl; }Singleton(const Singleton&) = delete;//防止外部使用拷贝构造产生新的对象,如下面Singleton s = *p1;Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::single;int main()
{Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();Singleton* p3 = Singleton::getInstance();cout << p1 << " " << p2 << " " << p3 << endl;return 0;
}
三、懒汉式单例模式
class Singleton
{
public:static Singleton* getInstance(){if (instance == nullptr) {instance = new Singleton();}return instance;}
private:static Singleton* instance;Singleton() { cout << "Singleton()" << endl; }~Singleton() { cout << "~Singleton()" << endl; }Singleton(const Singleton&) = delete;//防止外部使用拷贝构造产生新的对象,如下面Singleton s = *p1;Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance = nullptr;
所以,懒汉式单例模式,顾名思义,就是对象的实例化,延迟到第一次使用它的时候。
四、线程安全的单例模式
首先考虑一下以上两种单例模式是否线程安全
- 饿汉单例模式的线程安全特性
饿汉单例模式中,单例对象定义成了一个static静态对象,它是在程序启动时,main函数运行之前就初始化好的,因此不存在线程安全问题,可以放心的在多线程环境中使用。 - 懒汉单例模式的线程安全特性
懒汉单例模式,获取单例对象的方法如下:
static Singleton* getInstance()
{if (instance == nullptr) {/*开辟内存构造对象给instance赋值*/instance = new Singleton();}return instance;
}
上述三个步骤独立,所以多线程情况下会出现竞态,需要加锁
static Singleton* getInstance()
{lock_guard<std::mutex> guard(mtx);if (instance == nullptr) {instance = new Singleton();}return instance;
}
但是上述锁的粒度有点大,导致单线程情况下也要不断加锁,如果放在判断条件内肯定也是不对,所以考虑双重判断
#include<iostream>
#include <mutex>
using namespace std;
std::mutex mtx;
class Singleton
{
public:static Singleton* getInstance(){if (instance == nullptr) {lock_guard<std::mutex> guard(mtx);if (instance == nullptr) {instance = new Singleton();}}return instance;}
private:static Singleton* volatile instance;Singleton() { cout << "Singleton()" << endl; }~Singleton() { cout << "~Singleton()" << endl; }Singleton(const Singleton&) = delete;//防止外部使用拷贝构造产生新的对象,如下面Singleton s = *p1;Singleton& operator=(const Singleton&) = delete;
};
Singleton* volatile Singleton::instance = nullptr;int main()
{Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();Singleton* p3 = Singleton::getInstance();cout << p1 << " " << p2 << " " << p3 << endl;return 0;
}
volatile
关键字在这里的作用就是为了线程A对共享变量改变后,其他线程能直接看到新的值,而不是自己的缓存。
五、C++11后出现的局部static对象
class Singleton
{
public:static Singleton* getInstance(){static Singleton instance;return &instance;}
private:Singleton() { cout << "Singleton()" << endl; }~Singleton() { cout << "~Singleton()" << endl; }Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};
对于static静态局部变量的初始化,编译器会自动对它的初始化进行加锁和解锁控制,使静态局部变量的初始化成为线程安全的操作,不用担心多个线程都会初始化静态局部变量,因此上面的懒汉单例模式是线程安全的单例模式!