c++设计模式①单例模式 1.懒汉式

1.定义

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。[DP]

2.理解

因为需要只有一个实例对象,最好的控制方式就是类自身来控制,而不是在其他类或者全局变量来控制实例对象的创建。

我对于定义的理解是,为了保证一个类仅有一个实例,所以必须一个访问它的全局访问点。保证一个类仅有一个实例,换言之就是类的构造不对外开放,由其他方式来提供给外部使用,即提供一个访问的全局访问点或者接口。

3.懒汉式(使用时再创建)

这样我们就可以大概获得Singleton类的大概结构:

#pragma once

class Singleton
{
private:  //①构造函数私有化
	Singleton(){};

public:  
	//②提供一个全局访问点,由它来提供给外部Singleton的指针
	//因为要实现在没有Singleton对象的情况下获取到Singleton的指针,所以使用static
	//这样就可以通过类名来获取到对象指针了
	static Singleton* GetInstance();

private: //③ 用来返回的唯一的实例指针
	static Singleton* m_instance;

};
#include "Singleton.h"


//类内的全局变量需要独立初始化
Singleton* Singleton::m_instance = nullptr;

//懒汉式 即 使用再加载
Singleton* Singleton::GetInstance()
{
    if (!m_instance)  //如果m_instance等于nullptr,进入
    {
        m_instance = new Singleton();  //构造函数私有的目的就在于此
    }
    return m_instance;
}

以上的单例模式在单线程是安全的,但是在多线程的时候会出现问题。假设有两个线程同时访问GetInstance函数,且同时走到if (!m_instance) 的时候,会都得到m_instance为nullptr的信息,这样就会各自都会创建对象,这样对象就不在唯一了,与单例模式的定义不符合。主要原因是在创建完成前,大家都可以去创建。

既然会同时访问,那就加锁,

#pragma once
#include <mutex>

class Singleton
{
private:  //①构造函数私有化
	Singleton() {};

public:  
	//②提供一个全局访问点,由它来提供给外部Singleton的指针
	//因为要实现在没有Singleton对象的情况下获取到Singleton的指针,所以使用static
	//这样就可以通过类名来获取到对象指针了
	static Singleton* GetInstance();

private: //③ 用来返回的唯一的实例指针
	static Singleton* m_instance;

	static std::mutex m_mutex;
};

#include "Singleton.h"


//类内的全局变量需要独立初始化
Singleton* Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;

//懒汉式 即 使用再加载
Singleton* Singleton::GetInstance()
{
    m_mutex.lock();
    if (!m_instance)  //如果m_instance等于nullptr,进入
    {
        m_instance = new Singleton();  //构造函数私有的目的就在于此
    }
    m_mutex.unlock();
    return m_instance;
}

这样就保证了单例模式的安全,但是现在的代码,每次GetInstance都需要加锁,这样带来的开销很大,会降低效率。所以就有了deouble-check机制的产生。再回顾我们刚刚说的,主要原因是在创建完成前,大家都可以去创建。但如果已经创建好,再有多线程访问,那就不会有这些问题。

所以我们要对后续访问去锁,对创建加锁:

#include "Singleton.h"


//类内的全局变量需要独立初始化
Singleton* Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;

//懒汉式 即 使用再加载
Singleton* Singleton::GetInstance()
{
    if (!m_instance)
    {
        m_mutex.lock();
        if (!m_instance)  //如果m_instance等于nullptr,进入
        {
            m_instance = new Singleton();  //构造函数私有的目的就在于此
        }
        m_mutex.unlock();
    }
    return m_instance;
}

这就是double-check模式,这样创建完成后的后续只要走第一个if判断,而且不会进if内加锁灯操作,这样就维护了后续的效率。

如果想要主动销毁到Singleton对象,可以给类添加一个Release函数,来完成对单例对象的主动释放。谨慎,可能会带来野指针的问题,因为可能其他地方还持有着指针,你这时候释放了指针的空间,那么持有者后续进行访问的时候会出现问题。

至此,懒汉式单例就结束了。

上一篇:单例模式


下一篇:单例模式