美文网首页
单例模式

单例模式

作者: 进击的Lancelot | 来源:发表于2020-08-07 19:40 被阅读0次

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式的最终目的是为了要实现对唯一实例受控访问。另外注意,由于子类对象中会包含有基类的子对象,因此使用单例模式的类不能够作为基类去派生其他的类。

单例模式结构图:


image.png

根据对对象进行初始化时机的不同,单例模式可以分为懒汉式和饿汉式

懒汉式

懒汉式单例模式是指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)

C++ 11 以前的标准

双检查锁实现懒汉式单例模式(Double-checking Lock)

class Singleton{
public:
    static Singleton* getInstance(){
    // 先检查对象是否存在
    if(m_instance==nullptr){
        Lock lock; //伪代码
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
    }
    return m_instance;
}
private:
    Singleton(); //私有构造函数,不允许使用者自己生成对象
    Singleton(const Singleton& other);
    static Singleton* m_instance; //静态成员变量 
};

在相当长的一段时间,迷惑了很多人,在2000年的时候才被人发现漏洞,而且在每种语言上都发现了。原因是内存读写的乱序执行(编译器的问题)。

分析:m_instance = new Singleton()这句话可以分成三个步骤来执行:

  1. 分配了一个Singleton类型对象所需要的内存。

  2. 在分配的内存处构造Singleton类型的对象。

  3. 把分配的内存的地址赋给指针m_instance

可能会认为这三个步骤是按顺序执行的,但实际上只能确定步骤1是最先执行的,步骤23却不一定。问题就出现在这。假如某个线程A在调用执行m_instance = new Singleton()的时候是按照1,3,2的顺序的,那么刚刚执行完步骤3Singleton类型分配了内存(此时m_instance就不是nullptr了)就切换到了线程B,由于m_instance已经不是nullptr了,所以线程B会直接执行return m_instance得到一个对象,而这个对象并没有真正的被构造!!严重bug就这么发生了。

C++ 11中的版本

C++11当中明确保证了 static local 对象是线程安全的,同时由于 static local object 是存放于静态区,因此不会出现内存泄漏的问题

public:
    static Singleton& GetInstance(){
        static Singleton s;
        return s;
 }
private:
    Singleton(){}
    ~Singleton(){}
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;
};

饿汉式

饿汉式是指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

class Singleton
{
private:
    static Singleton instance;
    Singleton();
    ~Singleton();
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static Singleton& getInstance() {
        return instance;
    }
}
// initialize defaultly
Singleton Singleton::instance;

在饿汉式单例模式中,instance 是全局静态对象,因此初始化的过程将会在 main 之前的单线程启动阶段,因此不存在线程安全问题。

补充内容:static 对象的初始化

全局 static 对象(函数外)

C++规定,non-local static 对象的初始化发生在main函数执行之前,也即main函数之前的单线程启动阶段,所以不存在线程安全问题。但C++没有规定多个non-local static 对象的初始化顺序,尤其是来自多个编译单元的non-local static对象,他们的初始化顺序是未定义的

局部 static 对象(函数内)

对于local static 对象,其初始化发生在控制流第一次执行到该对象的初始化语句时。多个线程的控制流可能同时到达其初始化语句。

在C++11之前,在多线程环境下local static对象的初始化并不是线程安全的。具体表现就是:如果一个线程正在执行local static对象的初始化语句但还没有完成初始化,此时若其它线程也执行到该语句,那么这个线程会认为自己是第一次执行该语句并进入该local static对象的构造函数中。这会造成这个local static对象的重复构造,进而产生内存泄露问题。所以,local static对象在多线程环境下的重复构造问题是需要解决的。

而C++11则在语言规范中解决了这个问题。C++11规定,在一个线程开始local static 对象的初始化后到完成初始化前,其他线程执行到这个local static对象的初始化语句就会等待,直到该local static 对象初始化完成。

相关文章

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 单例模式

    单例模式1 单例模式2

  • java的单例模式

    饿汉单例模式 懒汉单例模式

网友评论

      本文标题:单例模式

      本文链接:https://www.haomeiwen.com/subject/knjqdktx.html