游戏开发设计模式:单例模式

2022年9月18日08:16:57

一、单例模式

1.1 单例模式总结介绍

1.1.1什么是单例模式

单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。

1.1.2单例模式分类

单例模式可以分为懒汉式和饿汉式,两者之间的区别在于创建实例的时间不同:

  • 懒汉式: 指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
  • 饿汉式: 指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

1.1.3单例类特点

  • 构造函数和析构函数为private类型,目的禁止外部构造和析构
  • 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
  • 类里有个可以全局访问的获取实例的静态函数

1.1.4如何保证线程安全?

  • 加锁访问
  • 进程开始的时候直接初始化,不管是否使用(这个只确保在初始化的时候只有一份实例)

1.2 单例模式的几种写法

1.2.1普通懒汉式单例 ( 线程不安全 )

classSingleton{public:// 外部接口,获取单例对象指针static Singleton*GetInstance();// 释放单例,进程退出时调用staticvoiddeleteInstance();private:// 将其构造和析构成为私有的, 禁止外部构造和析构,后续代码就不再重复添加这块Singleton();~Singleton();// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值Singleton(const Singleton&signal);const Singleton&operator=(const Singleton&signal);private:// 唯一单例对象指针static Singleton*m_SingleInstance;};//初始化静态成员变量
Singleton*Singleton::m_SingleInstance=NULL;
Singleton*Singleton::GetInstance(){if(m_SingleInstance==NULL){
		m_SingleInstance=new(std::nothrow) Singleton;// 没有加锁是线程不安全的,当线程并发时会创建多个实例}return m_SingleInstance;}void Singleton::deleteInstance(){if(m_SingleInstance){delete m_SingleInstance;
        m_SingleInstance=NULL;}}

1.2.2加锁的懒汉式单例 ( 线程安全 )

classSingleton{public:// 获取单实例对象static Singleton*GetInstance();//释放单实例,进程退出时调用staticvoiddeleteInstance();private:// 将其构造和析构成为私有的, 禁止外部构造和析构...private:// 唯一单实例对象指针static Singleton*m_SingleInstance;static std::mutex m_Mutex;// 锁};//初始化静态成员变量
Singleton*Singleton::m_SingleInstance=NULL;
std::mutex Singleton::m_Mutex;
Singleton* Singleton::GetInstance(){//  这里使用了两个 if判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,//  避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。if(m_SingleInstance==NULL){
        std::unique_lock<std::mutex>lock(m_Mutex);// 加锁if(m_SingleInstance==NULL){
            m_SingleInstance=new(std::nothrow) Singleton;}}return m_SingleInstance;}void Singleton::deleteInstance(){
    std::unique_lock<std::mutex>lock(m_Mutex);// 加锁,避免释放两次if(m_SingleInstance){delete m_SingleInstance;
        m_SingleInstance=NULL;}}

1.2.3内部静态变量的懒汉单例(C++11 线程安全)

classSingleton{public:// 获取单实例对象static Singleton&GetInstance();private:// 禁止外部构造Singleton();// 禁止外部析构~Singleton();// 禁止外部复制构造Singleton(const Single&signal);// 禁止外部赋值操作const Singleton&operator=(const Singleton&Singleton);};
Singleton& Singleton::GetInstance(){// 局部静态特性的方式实现单实例static Singleton signal;return signal;}

1.2.4饿汉式单例 (本身就线程安全)

classSingleton{public:// 获取单实例static Singleton*GetInstance();// 释放单实例,进程退出时调用staticvoiddeleteInstance();private:// 将其构造和析构成为私有的, 禁止外部构造和析构..private:// 唯一单实例对象指针static Singleton*g_pSingleton;};// 代码一运行就初始化创建实例 ,本身就线程安全
Singleton* Singleton::g_pSingleton=new(std::nothrow) Singleton;
Singleton* Singleton::GetInstance(){return g_pSingleton;}void Singleton::deleteInstance(){if(g_pSingleton){delete g_pSingleton;
        g_pSingleton=NULL;}}

1.3 单例模式的优缺点

单例模式就像一个被封装在类里面的全局变量,所以全局变量有的缺点它都有:多线程不友好代码耦合度高追踪状态变化困难
但是单例模式的有点还是很明显的,就是访问方便,直接使用全局唯一访问接口就能访问到类。只要包含头文件谁都可以进行访问,没有限制。
便利的访问是使用单例的主要原因,能够让随时随地获取所需的对象

1.4 在游戏中的应用

游戏中的许多单例类都是Manager类型功能,通常这些“管理类”的功能就是管理其他对象,或者就是一个工具类,比如专门写日志的。。
当需要管理游戏内一类对象的时候,只需要一个全局Manager类型 的保姆就可以了。
在确定使用单例模式前,需要确定是否类实例化出的对象是否是一定是全局唯一的。
就比如一个游戏中,写日志的类只有一个就可以了,那么就声明一个全局访问写日志的接口。
或者是游戏资源数据的管理,在其他例如窗口类中,如果需要这个资源,那可以直接去访问这个全局接口,而不是说在类里面自己再重新加载一份。

  • 在游戏中,可能会有很多需要单例实现的帮助类,或者工具类,如果不希望每个类都写一遍单例模式,就可以使用模板去实现单例,然后让工具类们继承这个模板类
template<typename T>classSingleton{public:static T&GetInstance(){static T instance;return instance;}Singleton(T&&)=delete;Singleton(const T&)=delete;voidoperator=(const T&)=delete;protected:Singleton()=default;virtual~Singleton()=default;};classFoo:public Singleton<Foo>{public:voidoperator()(){
        cout<<&GetInstance()<< endl;}};
  • 作者:除夕是我的猫
  • 原文链接:https://blog.csdn.net/qq_38493448/article/details/123384795
    更新时间:2022年9月18日08:16:57 ,共 3577 字。