设计模式-单例模式

一、简介

常用场景:日志模块,数据库模块

单例模式指的是一个类无论创建多少次对象,永远只能得到该类型一个对象的实例。

实现条件:

  1. 构造函数私有化,禁止用户任意创建对象
  2. 定义该类唯一的实例对象
  3. 通过一个static静态成员方法返回唯一的对象实例
  4. 禁用拷贝构造和拷贝复制运算符

二、饿汉式单例模式

cpp 复制代码
#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;
}

三、懒汉式单例模式

cpp 复制代码
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;

所以,懒汉式单例模式,顾名思义,就是对象的实例化,延迟到第一次使用它的时候。

四、线程安全的单例模式

首先考虑一下以上两种单例模式是否线程安全

  1. 饿汉单例模式的线程安全特性
    饿汉单例模式中,单例对象定义成了一个static静态对象,它是在程序启动时,main函数运行之前就初始化好的,因此不存在线程安全问题,可以放心的在多线程环境中使用。
  2. 懒汉单例模式的线程安全特性
    懒汉单例模式,获取单例对象的方法如下:
cpp 复制代码
static Singleton* getInstance()
{
	if (instance == nullptr) {
		/*
		开辟内存
		构造对象
		给instance赋值
		*/
		instance = new Singleton();
	}
	return instance;
}

上述三个步骤独立,所以多线程情况下会出现竞态,需要加锁

cpp 复制代码
static Singleton* getInstance()
{
	lock_guard<std::mutex> guard(mtx);
	if (instance == nullptr) {
		instance = new Singleton();
	}
	return instance;
}

但是上述锁的粒度有点大,导致单线程情况下也要不断加锁,如果放在判断条件内肯定也是不对,所以考虑双重判断

cpp 复制代码
#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对象

cpp 复制代码
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静态局部变量的初始化,编译器会自动对它的初始化进行加锁和解锁控制,使静态局部变量的初始化成为线程安全的操作,不用担心多个线程都会初始化静态局部变量,因此上面的懒汉单例模式是线程安全的单例模式!

相关推荐
Mr_WangAndy27 分钟前
C++设计模式_行为型模式_状态模式State
c++·设计模式·状态模式
bkspiderx2 小时前
C++设计模式之行为型模式:访问者模式(Visitor)
c++·设计模式·访问者模式
Lei活在当下2 小时前
【业务场景架构实战】8. 订单状态流转在 UI 端的呈现设计
android·设计模式·架构
Query*2 小时前
Java 设计模式——代理模式:从静态代理到 Spring AOP 最优实现
java·设计模式·代理模式
杯莫停丶20 小时前
设计模式之:简单工厂模式
java·设计模式·简单工厂模式
kyle~21 小时前
设计模式---观察者模式
服务器·观察者模式·设计模式
Query*1 天前
Java 设计模式——适配器模式进阶:原理深挖、框架应用与实战扩展
java·设计模式·适配器模式
Meteors.1 天前
23种设计模式——中介者模式 (Mediator Pattern)详解
java·设计模式·中介者模式
Query*1 天前
Java 设计模式——适配器模式:从原理到3种实战的完整指南
java·设计模式·适配器模式
Meteors.1 天前
23种设计模式——状态模式(State Pattern)
java·设计模式·状态模式