单例模式

单例类除了只能创建一个该类对象外,在使用方面与普通类没什么区别

#include <atomic>
#include <mutex>

// 游戏配置相关类
class GameConfig {
    private:
        GameConfig() {};
        GameConfig(const GameConfig& tmpobj);
        GameConfig& operator=(const GameConfig& tmpobj);
        ~GameConfig() {};
    public:
        static GameConfig* getInstance() {
            GameConfig* tmp = m_instance.load(std::memory_order_relaxed);
            std::atomic_thread_fence(std::memory_order_acquire);
            if(tmp == nullptr) {
                std::lock_guard<std::mutex> lock(m_mutex);
                tmp = m_instance.load(std::memory_order_relaxed);
                if(tmp == nullptr) {
                    tmp = new GameConfig();
                    std::atomic_thread_fence(std::memory_order_release);
                    m_instance.store(tmp, std::memory_order_relaxed);
                }
            }
            return tmp;
        }
    
    private:
        static atomic<GameConfig*> m_instance;
        static std::mutex m_mutex;
};

std::atomic<GameConfig*> GameConfig::m_instance;
std::mutex GameConfig::m_mutex;

volatile关键字作用

在这里插入图片描述

std::atomic

在这里插入图片描述

std::memory_order_relaxed

在这里插入图片描述

GameConfig* tmp = m_instance.load(std::memory_order_relaxed);

在这里插入图片描述

std::memory_order_acquire

在这里插入图片描述

std::lock_guard<std::mutex> lock(m_mutex);

在这里插入图片描述

std::memory_order_release和std::memory_order_relaxed的区别

在这里插入图片描述

m_instance.store(tmp, std::memory_order_relaxed);

在这里插入图片描述

饿汉模式

程序一执行,不管是否调用了getInstance成员函数,这个单件类对象就已经被创建了(对象创建将不受多线程问题困扰)

对饿汉式单例类对象的使用,应该在程序入口函数开始执行后,例如main主函数开始执行后

//饿汉式
class GameConfig
{
    //......
private:
    GameConfig() {};
    GameConfig(const GameConfig& tmpobj);
    GameConfig& operator=(const GameConfig& tmpobj);
    ~GameConfig() {};
public:
    static GameConfig* getInstance()
    {
        return m_instance;
    }
private:
    static GameConfig* m_instance;	 //指向本类对象的指针

private:
    //手工释放单例类对象引入的GameConfig类中的嵌套类(垃圾回收)
    class Garbo {
    public:
        ~Garbo() {
            if (GameConfig::m_instance != nullptr)
            {
                delete GameConfig::m_instance;
                GameConfig::m_instance = nullptr;
            }
        }
    };

private:
    static Garbo garboobj;
};

GameConfig* GameConfig::m_instance = new GameConfig();//趁静态成员变量定义的时机直接初始化是被允许的,即便GameConfig构造函数用private修饰
GameConfig::Garbo GameConfig::garboobj;

懒汉模式

程序执行后该单例类对象并不存在,只有第一次调用getInstance成员函数时,该单例类对象才会被创建,这种方式能够更好的控制单例类的创建时机,以免过早加载可能导致对内存等资源不必要的消耗(加入类GameConfig非常庞大的话)

//懒汉式
//游戏配置相关类
class GameConfig
{
    //......
private:
    GameConfig() {};
    GameConfig(const GameConfig& tmpobj);
    GameConfig& operator = (const GameConfig& tmpobj);
    ~GameConfig() {};
public:
    static GameConfig* getInstance()
    {
        //std::lock_guard<std::mutex> gcguard(my_mutex);
        if (m_instance == nullptr)
        {
            //这里再加锁
            //std::lock_guard<std::mutex> gcguard(my_mutex);
            //if (m_instance == nullptr)
            //{
            m_instance = new GameConfig();
            static Garbo garboobj;
            //}
        }
        return m_instance;
    }

public:
    //要手工调用才能释放内存
    static void freeInstance()
    {
        if (m_instance != nullptr)
        {
            delete GameConfig::m_instance;
            GameConfig::m_instance = nullptr;
        }
    }

private:
    //手工释放单件类对象引入的GameConfig类中的嵌套类(垃圾回收)
    class Garbo {
    public:
        ~Garbo() {
            if (GameConfig::m_instance != nullptr)
            {
                delete GameConfig::m_instance;
                GameConfig::m_instance = nullptr;
            }
        }
    };

private:
    static GameConfig* m_instance; //指向本类对象的指针 
    static Garbo garboobj;
};

GameConfig* GameConfig::m_instance = nullptr; //在类外,某个.cpp源文件的开头位置,为静态成员变量赋值(定义并赋值)
GameConfig::Garbo GameConfig::garboobj;

懒汉模式(进阶版)

//懒汉式
//游戏配置相关类 
class GameConfig
{
private:
    GameConfig() {};
    GameConfig(const GameConfig& tmpobj);
    GameConfig& operator = (const GameConfig& tmpobj);
    ~GameConfig() {};
public:
    static GameConfig* getInstance()
    {
        GameConfig* tmp = m_instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);
        if (tmp == nullptr)
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr)
            {
                tmp = new GameConfig();
                static Garbo garboobj;
                std::atomic_thread_fence(std::memory_order_release);							
                m_instance.store(tmp, std::memory_order_relaxed);
            }
        }
        return tmp;
    }

private:
    //手工释放单件类对象引入的GameConfig类中的嵌套类(垃圾回收)
    class Garbo {
    public:
        ~Garbo() {
            if (GameConfig::m_instance != nullptr)
            {
                delete GameConfig::m_instance;
                GameConfig::m_instance = nullptr;
            }
        }
    };

private:
    static atomic<GameConfig*> m_instance;
    static std::mutex m_mutex;
};

std::atomic<GameConfig*> GameConfig::m_instance;
std::mutex GameConfig::m_mutex; 

单例类UML图

引入单例设计模式的实现意图:保证一个类仅有一个实例存在,同时提供能对该实例访问的全局方法(getInstance成员函数)

在这里插入图片描述

重点

不要在单例类的析构函数中引用其他单例类对象

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐