定义
- 类的实例只有一个
特点
- 构造函数私有化,在类内部只调用一次
- 拷贝构造函数私有化或者禁用
饿汉单例
- 类加载的时候立刻进行实例化
- 定义这个单例类的时候,就把这个静态的单例对象创建出来了
example
// 饿汉模式
class TaskQueue
{
public:// = delete 代表函数禁用, 也可以将其访问权限设置为私有TaskQueue(const TaskQueue& obj) = delete;TaskQueue& operator=(const TaskQueue& obj) = delete;static TaskQueue* getInstance(){return m_taskQ;}
private:TaskQueue() = default;static TaskQueue* m_taskQ;
};
// 静态成员初始化放到类外部处理
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
懒汉单例
- 类加载的时候不去创建这个唯一的实例,而是在需要使用的时候再进行实例化
example
// 懒汉模式
class TaskQueue
{
public:// = delete 代表函数禁用, 也可以将其访问权限设置为私有TaskQueue(const TaskQueue& obj) = delete;TaskQueue& operator=(const TaskQueue& obj) = delete;static TaskQueue* getInstance(){if(m_taskQ == nullptr){m_taskQ = new TaskQueue;}return m_taskQ;}
private:TaskQueue() = default;static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;
缺点
- 多线程环境下,可能生成多个单例
- 在调用
getInstance()
函数获取单例对象的时候,如果在单线程情况下是没有什么问题的,如果是多个线程,调用这个函数去访问单例对象就有问题了。假设有三个线程同时执行了getInstance()
函数,在这个函数内部每个线程都会new
出一个实例对象。此时,这个任务队列类的实例对象不是一个而是3个,很显然这与单例模式的定义是相悖的
- 在调用
解决方案
- 加锁
- 局部静态变量
class TaskQueue
{
public:// = delete 代表函数禁用, 也可以将其访问权限设置为私有TaskQueue(const TaskQueue& obj) = delete;TaskQueue& operator=(const TaskQueue& obj) = delete;static TaskQueue* getInstance(){static TaskQueue taskQ;return &taskQ;}void print(){cout << "hello, world!!!" << endl;}private:TaskQueue() = default;
};int main()
{TaskQueue* queue = TaskQueue::getInstance();queue->print();return 0;
}
在程序的第 9、10 行定义了一个静态局部队列对象,并且将这个对象作为了唯一的单例实例。使用这种方式之所以是线程安全的,是因为在C++11标准中有如下规定,并且这个操作是在编译时由编译器保证的:
如果指令逻辑进入一个未被初始化的声明变量,所有并发执行应当等待该变量完成初始化。
PS:局部静态变量
- 在函数内部进行初始化,且只在第一次进入函数时执行初始化操作。初始化方式和普通局部变量类似,只是因为它是静态的,后续函数调用时不会再次初始化。