常见的设计模式--单例模式

设计模式

  设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的是为了代码可重用性、让代码更容易被他人理去解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式

  一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

饿汉模式

  饿汉模式就是在程序运行之前将所有的资源一次性创建好,在之后用的时候就会很方便。由于静态的变量是在进入主函数之前就创建好了,所以要定义一个静态变量,然后将构造函数和拷贝构造设置为私有的,拷贝构造函数要只声明不定义,并在类中给出一个创建对象的静态方法,这个静态方法只能是引用返回,如果值返回,就会调用拷贝构造,但是拷贝构造调不了,即使可以调就会创建一个对象,这样就和单例模式相冲突。虽然这种方式在程序运行之后不用再去创建资源,直接调用就可以,但是如果这份资源很大,那么程序就会启动的很慢。

class Singleton 
{
public:
	static Singleton* GetInstance()
	{
		return &p;
	}

private:
	//构造函数私有
	Singleton()
	{}
	//防止被拷贝
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	static Singleton p;
};

Singleton Singleton::p;

  使用场景

  在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞
争,提高响应速度更好。

懒汉模式

  实现过程:
  懒汉模式就是在需要的时候才会去创建 构造函数和拷贝构造函数都要设为私有的,同样的在类中给出一个创建静态的方法,并且在类中创建一个静态的指针,在调用这个方法时如果这个静态的指针是空的,就为这个指针new一个对象,返回去。如果是在多线程的环境中,就会出现多个线程同时去申请资源,这时候就要多线程中常用的加解锁操作,如果每次都要先加锁在检测,这时候所有的线程都会阻塞在加锁这里,等解锁后才可以继续操作,所以使用DCL双检锁,这时如果有一个线程在进行加解锁操作,另一个线程也过来了,这时候资源已经申请好了,这个后来的线程就可以直接返回,不用阻塞等待之前的线程。但是这样的版本仍然是有问题的,如果线程A正在进行对象的的创建,线程B过来了,但是A现在只是申请了空间,还没有进行实例化,但是B检测到这个对象不为空,直接返回去使用,就会出问题,所以要将静态对象加一个volatile来限定一下,告诉系统每次取变量里面的信息的时候不要从寄存器中取,要到内存里面取,这样就禁止了编译器对创建对象的次序进行优化(申请空间->构造对象->赋值—>申请空间->赋值->构造对象)。虽然改进了这么多,但是这个代码还存在问题,那就是没有释放空间,可能会存在内存泄漏。如果要释放,就要保证所有的线程已经用完了这份资源,但是不能在类中直接给出一个静态的释放函数,这样有可能会忘记调用这个函数,最好的方法就是内嵌一个内部类来实现释放。

#include <mutex>
#include <thread>

using namespace std;

class Singleton1
{
public:
	volatile Singleton1* GetInstance()
	{
		if (p == nullptr)//要采用DCL双检锁,让其他的线程可以不用等,直接返回。
			//这样如果编译器对代码进行了优化,将创建对象的顺序重新调整,直接返回就会出错。
		{
			m_tex.lock();//如果这里只加这一个锁,然后去判断,其他线程会阻塞在这里等待解锁。
			if (p == nullptr)
				 p = new Singleton1;
			m_tex.unlock();
		}

		return p;
	}

	//在释放资源的时候要保证所有线程已经将这份资源用完,但是不能直接在类中给出一个释放资源的函数,有可能忘记调用这个函数
	//最好的方法是在类中内嵌一个类负责资源释放
	class Clean
	{
	public:
		~Clean()
		{
			if (Singleton1::p)
			{
				delete Singleton1::p;
				Singleton1::p = nullptr;
			}
		}

	};

	static Clean c;

private:
	Singleton1()
	{}
	Singleton1(const Singleton1&) = delete;
	Singleton1& operator=(const Singleton1&) = delete;
private:
	static  Singleton1 volatile *p;
	static mutex m_tex;
};
//为对象添加volatile关键字,告诉系统取变量里面的信息的时候从内从中取,这样就禁止了编译器对变量的创建顺序进行优化
//但是这样还不够,就是没有释放空间,会造成内存泄漏。
volatile Singleton1* Singleton1::p = nullptr;
mutex Singleton1::m_tex;
Singleton1::Clean c;

  使用场景

  如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值