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

发布于:2024-06-02 ⋅ 阅读:(141) ⋅ 点赞:(0)


运行在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;
}
  • 反汇编分析如下。

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到