• 单例模式-Singleton


    摘要:今天看面试会问到单例模式,今天我们就来学习一下。

    介绍

      意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

      主要解决:一个全局使用的类的频繁地创建与销毁。

      使用场景:当想控制实例数目,节省资源的时候。

      关键代码:构造函数是私有的;拷贝构造函数是私有的;局部变量是静态的。

    实现

      不支持并发

    /*
     * method-1
     * 延迟初始化,只有在第一次调用 getInstance 的时候才会初始化实例
     * 存在问题:线程不安全版本,可能有多个线程在 if 条件处产生竞态条件,从而产生多个实例
     * */
    class Singleton {
    public:
        static Singleton* getInstance() {
            //先检查对象是否存在
            if(m_instance == nullptr) {
                m_instance = new Singleton();
            }
            return m_instance;
        }
    private:
        //构造函数私有化
        Singleton();
        //析构函数私有化
        ~Singleton();
        //拷贝构造函数私有化
        Singleton(const Singleton& other);
        //赋值函数私有化
        Singleton& operator = (const Singleton &);
        //静态局部变量
        static Singleton* m_instance;
    };
    
    //静态成员需要在类外赋值
    Singleton* Singleton::m_instance = nullptr;

      支持并发[加锁]

    /*
     * method-2
     * 线程安全版本
     * 存在问题:但锁的代价过高
     * */
    class Singleton {
    public:
        static Singleton* getInstance() {
            Lock lock; //加锁,此处为伪代码
            if(m_instance == nullptr) {
                m_instance = new Singleton();
            }
            unlock;
            return m_instance;
        }
    private:
        Singleton();
        ~Singleton();
        Singleton(const Singleton& other);
        Singleton& operator = (const Singleton &);
        static Singleton* m_instance;
    };
    
    //静态成员需要在类外赋值
    Singleton* Singleton::m_instance = nullptr;

      双检查锁

    /*
     * method-3
     * 线程安全版本,双检查锁
     * 存在问题:产生内存读写乱序问题
     * 分析:实际上 m_instance = new Singleton() 这条语句是分三个步骤来执行的
     * (1) 分配了一个 Sington 类型对象所需要的内存
     * (2) 再分配的内存出构造 Singleton 类型的对象
     * (3) 把分配的内存地址赋给指针 m_instance
     * 编译器会给我们做些优化,其实这三个步骤不一定是顺序执行的,只能确定步骤(1) 是最先执行的,步骤(2)(3) 却不一定。
     * 如果线程 A 在调用执行 m_instance = new Singleton() 的时候是按照 (1)(3)(2)的顺序执行的,那么刚刚执行完(3)给 m_instance 分配了内存
     * 此时 m_instance 就不再是 nullptr
     * 如果此时切换到了线程B, 由于 m_instance != nullptr, 所以线程B会直接执行 return m_instance 得到一个对象,而这个对象并没有被真正构造
     * 解决方式:在声明 m_instance 变量的时候,要加上 volatile 关键字修饰
     * */
    class Singleton {
    public:
        Singleton* getInstance() {
            if(m_instance == nullptr) {
                Lock lock;
                if(m_instance == nullptr) {
                    m_instance = new Singleton();
                }
                unlock;
            }
        }
    
    private:
        Singleton();
        ~Singleton();
        Singleton(const Singleton& other);
        Singleton& operator = (const Singleton &);
        static Singleton* m_instance;
    };
    
    //静态成员需要在类外赋值
    Singleton* Singleton::m_instance = nullptr;

      C++11 极简实现

    /*
     * method-4
     * 最为简单的实现方式
     * 存在问题:C++11及以后的版本下才正确,之前的版本不能这么写,原因是 C++11 在底层可以保证 static 变量是多线程安全的
     * */
    class Singleton {
    public:
        Singleton* getInstance() {
            static Singleton instance;
            return &instance;
        }
    private:
        Singleton();
        ~Singleton();
        Singleton(const Singleton& other);
        Singleton& operator = (const Singleton &);
    };
  • 相关阅读:
    系统并发报too much open files 错误
    plsql 安装Some Oracle Net versions cannot connect from a path with parentheses
    mysql登录报错
    web.xml配置文件详解之一servlet配置
    hibernate createQuery查询传递参数的两种方式
    mysql登录连接远程数据库命令行
    java 项目打包部署 过程
    flex ArrayCollection的新增与删除的同步
    加大eclipse以及jvm的内存
    2017 八月 UFED Series Releases 系列 6.3 重大更新发布
  • 原文地址:https://www.cnblogs.com/zpcoding/p/13226791.html
Copyright © 2020-2023  润新知