设计模式之单例模式

模式定义

单例模式(Singleton Pattern)定义:单例模式确保一个类只有一个实例,而且自行实例化并向系统提供这个实例,这个类成为单例类,它提供全局访问的方法。单例模式的要点有三个:一个是这个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。 单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

模式分析

单例模式只包含一个 Singleton(单例角色):在单例类得内部实现只生成一个实例,同时它提供一个静态得 getInstance()工厂方法,让客户可以使用它得唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型得静态对象,作为外部共享得唯一实例。

单例模式得目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含得角色只有一个,就是单例类——Singleton。单例类拥有一个私有的构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

一般情况下,单例模式的实现代码如下:

class Singleton{
public:
	//静态共有工厂方法,返回唯一实例
	static Singleton* GetInstance()
	{
		if(instance == nullptr)
			instance = new Singleton();
		return &m_instance;
	}
private:
	static Singleton instance = nullptr; //静态私有成员变量
	//私有构造函数
	static Singleton() {
	
	}
};

模式优缺点

优缺点

优点

  1. 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
  2. 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
  3. 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

缺点

  1. 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  2. 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类即充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品本身的功能融合到一起。

模式适用环境

在以下情况可以使用单例模式:

  1. 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
  2. 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

使用单例模式有一个必要条件:在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式。

在使用的过程中我们还需要注意一下两个问题:

  1. 不要使用单例模式存取全局变量,因为这违背了单例模式的用意,最好将全局变量放到对应的静态成员中
  2. 不要将数据库连接做成单例,因为一个系统可能会与多个数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。单例模式由于使用静态成员变量存储类的实例,所以可能会造成资源无法及时释放,带来一些问题

单例模式扩展

饿汉模式

// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return &m_instance;
	}
private:
	// 构造函数私有
	Singleton(){};
	// C++98 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);
	// or
	// C++11
	Singleton(Singleton const&) = delete;
	Singleton& operator=(Singleton const&) = delete;
	static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化

懒汉模式

// 懒汉
// 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
// 缺点:复杂
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
public:
	static Singleton* GetInstance() {
	// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
	if (nullptr == m_pInstance) {
		m_mtx.lock();
		if (nullptr == m_pInstance) {
			m_pInstance = new Singleton();
		}
		m_mtx.unlock();
	}
	return m_pInstance;
}
	// 实现一个内嵌垃圾回收类
	class CGarbo {
	public:
		~CGarbo(){
			if (Singleton::m_pInstance)
				delete Singleton::m_pInstance;
			}
	};
	// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
	static CGarbo Garbo;
private:
	// 构造函数私有
	Singleton(){};
	// 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);
	static Singleton* m_pInstance; // 单例对象指针
	static mutex m_mtx; //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;

对比

饿汉模式在自己被加载时就将自己实例化。但从资源利用率角度来讲,这个比懒汉模式稍微差些。从速度和反应时间角度来讲,则比懒汉模式稍微好些。然而懒汉模式在实例化时,必须处理好多个线程同时首次引用此类的访问限制问题,特别是当单例模式作为资源控制器,在实例化时必须涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着多个线程同时首次引用此类的几率变得比较大,需要通过同步化机制进行控制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值