• C++设计模式-单例模式,反汇编



    运行在VS2022,x86,Debug下。

    25. 单例模式

    • 单例即该类只能有一个实例。

    • 应用:如在游戏开发中,可以使用单例模式来管理各种资源,确保这些资源在整个游戏中只被加载和管理一次。

    • 实现

      • 将构造函数和析构函数私有,不允许外部构造或析构类对象。
      • 将拷贝构造函数、赋值运算符、移动构造函数和移动赋值运算符删除,不允许复制或移动类对象。
      • 需要有一个静态函数接口,返回唯一的静态实例。
    • 分类

      • 饿汉式单例模式:在main()开始前,实例就已经存在了。
      • 懒汉式单例模式:在第一次调用获取实例时才创建实例。

    25.1. 饿汉式单例模式

    • 代码如下。
    class Singleton
    {
    private:
        Singleton() {} //私有构造函数
        ~Singleton() {} //私有析构函数
    
        Singleton(const Singleton&) = delete; //删除拷贝构造函数
        Singleton& operator=(const Singleton&) = delete; //删除赋值运算符
        Singleton(Singleton&&) = delete; //删除移动构造函数
        Singleton & operator=(Singleton&&) = delete; //删除移动赋值运算符
     
    private:
        static Singleton instance; //静态成员变量,存储实例
    
    public:
        static Singleton* getInstance() //静态成员函数,获取实例
        {
            return &instance;
        }
    };
    
    Singleton Singleton::instance; //静态成员变量实例化
    
    int main()
    {
        Singleton* s1 = Singleton::getInstance();
        Singleton* s2 = Singleton::getInstance();
        return 0;
    }
    
    • 在main()处设置断点,监视窗口如下,实例的内存地址为0x00E0C138。

    在这里插入图片描述

    • 总结
      • 优点:线程安全,因为程序运行时就已经生成唯一的实例。
      • 缺点:不是按需创建实例。

    25.2. 懒汉式单例模式

    • 新增一个静态函数接口,用于释放实例内存。
    • 代码如下。
    class Singleton
    {
    private:
        Singleton(){} //私有构造函数
        ~Singleton(){} //私有析构函数
     
        Singleton(const Singleton&) = delete; //删除拷贝构造函数
        Singleton& operator=(const Singleton&) = delete; //删除赋值运算符
        Singleton(Singleton&&) = delete; //删除移动构造函数
        Singleton & operator=(Singleton&&) = delete; //删除移动赋值运算符
     
    private:
        static Singleton* instance; //静态成员变量,存储实例
     
    public:
        static Singleton* getInstance() //静态成员函数,获取实例
        {
            if (instance == nullptr)
                instance = new Singleton();
            return instance;
        }
     
        static void deleteInstance() //静态成员函数,释放实例
        {
            if (instance != nullptr)
            {
                delete instance;
                instance = nullptr;
            }
        }
    };
     
    Singleton* Singleton::instance = nullptr; //静态成员变量定义和初始化
     
    int main() 
    {
        Singleton* s1 = Singleton::getInstance();
        Singleton* s2 = Singleton::getInstance();
     
        Singleton::deleteInstance();
        return 0;
    }
    
    • 在main()处设置断点,监视窗口如下,此时未生成实例。

    在这里插入图片描述

    • 第一次调用getInstance()获取实例时,生成实例,实例的内存地址为0x00E16A08。

    在这里插入图片描述

    • 总结
      • 优点:按需创建实例。
      • 缺点:
        • 不是线程安全,如两个线程同时调用getInstance()获取实例,同时运行到判断instance是否为nullptr的if语句,并且instance并未创建,那么两个线程都会创建一个实例。
        • 内存释放问题,在程序执行结束时,调用deleteInstance()释放实例内存,但是要确保delete之后,没有代码再调用getInstance()或者访问已释放的内存,存在安全隐患。

    25.2.1. 解决方案1

    • 针对线程安全问题,加锁。
    • 针对内存释放问题,对于全局变量或静态变量,main()返回后会调用析构函数。基于此,可以定义一个静态成员变量,用于释放实例内存。
    • 代码如下。
    mutex m;
    
    class Singleton
    {
    private:
        Singleton() {} //私有构造函数
        ~Singleton() {} //私有析构函数
    
        Singleton(const Singleton&) = delete; //删除拷贝构造函数
        Singleton& operator=(const Singleton&) = delete; //删除赋值运算符
        Singleton(Singleton&&) = delete; //删除移动构造函数
        Singleton& operator=(Singleton&&) = delete; //删除移动赋值运算符
    
        static Singleton* instance; //静态成员变量,存储实例
    
        class Garbo //在析构函数中释放实例
        {
        public:
            ~Garbo()
            {
                if (Singleton::instance != nullptr)
                {
                    delete instance;
                    instance = nullptr;
                }
            }
        };
    
        static Garbo garbo; //静态成员变量,在程序执行结束时,系统会调用它的析构函数
    
    public:
        static Singleton* getInstance() //静态成员函数,获取实例
        {
            if (instance == nullptr) //加锁前判断,这样如果实例存在,就不需加锁了
            {
                lock_guard<mutex>lock(m); //创建实例前加锁
                if (instance == nullptr)
                    instance = new Singleton();
            }
           
            return instance;
        }
    };
    
    Singleton* Singleton::instance = nullptr; //静态成员变量定义和初始化
    
    int main()
    {
        Singleton* s1 = Singleton::getInstance();
        Singleton* s2 = Singleton::getInstance();
    
        return 0;
    }
    

    25.2.2. 解决方案2 (推荐写法)

    • 在静态函数接口里,定义局部静态变量,存储实例。那么它会在第一次调用获取实例时才创建,可以按需创建实例。同时内部__Init_thread_header()和_Init_thread_footer()可以保证局部静态变量的初始化是线程安全的。
    • 代码如下。
    class Singleton
    {
    private:
        Singleton() {} //私有构造函数
        ~Singleton() {} //私有析构函数
    
        Singleton(const Singleton&) = delete; //删除拷贝构造函数
        Singleton& operator=(const Singleton&) = delete; //删除赋值运算符
        Singleton(Singleton&&) = delete; //删除移动构造函数
        Singleton& operator=(Singleton&&) = delete; //删除移动赋值运算符
       
    public:
        static Singleton* getInstance() //静态成员函数,获取实例
        {
            static Singleton instance; //局部静态成员变量,存储实例
            return &instance;
        }
    };
    
    int main()
    {
        Singleton* s1 = Singleton::getInstance();
        Singleton* s2 = Singleton::getInstance();
    
        return 0;
    }
    
    • 反汇编分析如下。

    在这里插入图片描述

  • 相关阅读:
    美国服务器租用详细介绍与租用流程
    Cpp/Qtday070914cpp基础
    C++ MAC获取所有进程和当前进程的CPU使用率
    Vue3+elementplus搭建通用管理系统实例七:通用表格实现上
    vue2 编写自己的组件库,并发布到npm
    Ubantu opencv安装
    79基于matlab的大米粒中杂质识别
    [OpenCv]初识——图像的基本处理
    javaVUE技术小区车辆档案车位管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    数据结构 --- 贪心算法
  • 原文地址:https://blog.csdn.net/qq_42692132/article/details/139292561