【C++】设计模式 之 单例模式(手撕代码)

什么是单例模式

单例模式是常见的一种软件设计模式,单例对象的类只能实例化一个对象

该类负责创建对象,同时保证只能创建一个对象。并提供一个访问它的全局访问点,该实例被所有程序模块共享。

一般应用与工具类的实现或者消耗资源的场景。

特点

  1. 类构造函数私有
  2. 持有自己类的引用
  3. 对外获取实例的静态方法

饿汉模式

实例在初始化已经建好,不管你用不用,先建好再说。

优点:没有线程安全的问题

缺点:浪费内存空间。

class singleton
{
public:
	//对外提供获取实例的静态方法
	static singleton* Getinstance()
	{
		return &m_instance;
	}
	//构造函数私有
private:
	singleton(){};

	//C++98 防拷贝
	//singleton(singleton const&);
	//singleton& operator=(singleton const&);
	
	//C++11 防拷贝
	singleton(singleton const&) = delete;
	singleton& operator=(singleton const&) = delete;

	static singleton m_instance;
};
//程序入口地址之前就完成对单例对象的初始化
singleton singleton::m_instance;

懒汉模式

用的时候才去检查有没有创建实例,如果有则返回,没有则新建。

template <typename T>
class Singleton {
	static T* inst;
public:
	static T* GetInstance() {
	if (inst == NULL) {
		inst = new T();
	}
	return inst;
	}
};

示例中的INSTANCE对象一开始是空的,在调用getInstance方法才会真正实例化。

上述代码有问题

假如多个线程都调用了getinstance方法,且都走到了判断为空那一步,可能同时成立。这样会导致多个线程同时创建instance对象,即创建了多次,出现线程不安全的情况

优点:没有内存空间浪费的问题。

缺点:控制不好不是单例。

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

class singleton
{
public:
	static singleton* Getinstance()
	{
	//双重检查锁。
	//加锁之前检查一次是否为空,加锁之后再检查一次。
		if (nullptr == m_instance)
		{
			m_mtx.lock();
			if (nullptr == m_instance)
			{
				m_instance = new singleton();
			}
			m_mtx.unlock();
		}
		return m_instance;
	}
	
	//实现一个内嵌垃圾回收类
	class CGarbo{
	public:
		~CGarbo(){
			if (singleton::m_instance)
				delete singleton::m_instance;
		}
	};
	//定义一个静态成员变量,程序结束时,会自动调用它的析构函数从而释放单例对象。
	static CGarbo Garbo;
private:
//构造函数私有
	singleton(){};
//防拷贝
	singleton(singleton const&);
	singleton& operator=(singleton const&);

	static singleton* m_instance;//单例对象指针
	static mutex m_mtx;//互斥锁
};
singleton* singleton::m_instance = nullptr;
singleton::CGarbo Garbo;
mutex singleton::m_mtx;

void func(int n)
{
	cout << singleton::Getinstance() << endl;
}

int mian()
{
	thread t1(func, 10);
	thread t2(func, 10);

	t1.join();
	t2.join();

	cout << singleton::Getinstance() << endl;
	cout << singleton::Getinstance() << endl;
}

加锁之前判断是否为空,可以确保instance不为空的情况,不用加锁,直接返回。

单例模式的优缺点

优点

  1. 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例

  2. 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
    提供了对唯一实例的受控访问。

  3. 由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。

  4. 允许可变数目的实例。

  5. 避免对共享资源的多重占用。

缺点

  1. 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

  2. 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。

  3. 单例类的职责过重,在一定程度上违背了“单一职责原则”。

  4. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

单例模式的适用场景

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:

  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 有状态的工具类对象。
  • 频繁访问数据库或文件的对象

在这里插入图片描述

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页