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

一、单例模式

1.1 单例模式总结介绍

1.1.1什么是单例模式

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

1.1.2单例模式分类

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

  • 懒汉式: 指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
  • 饿汉式: 指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)
1.1.3单例类特点
  • 构造函数和析构函数为private类型,目的禁止外部构造和析构
  • 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
  • 类里有个可以全局访问的获取实例的静态函数
1.1.4如何保证线程安全?
  • 加锁访问
  • 进程开始的时候直接初始化,不管是否使用(这个只确保在初始化的时候只有一份实例)

1.2 单例模式的几种写法

1.2.1普通懒汉式单例 ( 线程不安全 )
class Singleton
{
public:
    // 外部接口,获取单例对象指针
    static Singleton* GetInstance();
    // 释放单例,进程退出时调用
    static void deleteInstance();
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加锁的懒汉式单例 ( 线程安全 )
class Singleton
{
public:
    // 获取单实例对象
    static Singleton* GetInstance();
    //释放单实例,进程退出时调用
    static void deleteInstance();
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 线程安全)
class Singleton
{
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饿汉式单例 (本身就线程安全)
class Singleton
{
public:
    // 获取单实例
    static Singleton* GetInstance();
    // 释放单实例,进程退出时调用
    static void deleteInstance();
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>
class Singleton
{
public:
    static T& GetInstance()
    {
        static T instance;
        return instance;
    }
    Singleton(T&&) = delete;
    Singleton(const T&) = delete;
    void operator= (const T&) = delete;
protected:
    Singleton() = default;
    virtual ~Singleton() = default;
};

class Foo : public Singleton<Foo>
{
public:
    void operator() ()
    {
        cout << &GetInstance() << endl;
    }
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值