单例模式
作用及特点:单例类,顾名思义只会创建一个,例如太阳类,地球类, 一般来说单例类分为两种:懒汉模式和饿汉模式。
因为我们的马戏团比较小,只有一个马戏团管理员,所以我们将 马戏团管理员写成单例类,其实我们在第一篇《注册工厂模式》就使用到了单例模式,不过那个单例是全局函数获取单例,这次我们使用类内定义,开一个静态接口。
线程安全懒汉单例
懒汉模式是第一次调用接口的时候,第一次创建该单例类,因为实例化出来的是静态对象,所以只会被初始化一次。以后每次调用接口,返回值都是第一次调用时初始化的对象。
class AnimalManager
{
public:
static GetInterface();
private:
AnimalManager();
static AnimalManager* m_animalManager;
static std::mutex m_singleMutex;//单例模式的锁
};
线程安全懒汉单例具体实现,它要解决的问题是,多个线程同时第一次调用接口的时候,对创建操作加锁,防止创建两份,引起内存指针混乱的问题。
AnimalManager* AnimalManager::m_animalManager = nullptr;
std::mutex AnimalManager::m_singleMutex;
AnimalManager* AnimalManager::getAnimalManager()
{
if (nullptr == m_animalManager)
{
m_singleMutex.lock();
m_animalManager = new AnimalManager;
m_singleMutex.unlock();
}
return m_animalManager;
}
饿汉单例
懒汉模式是类初始化的时候创建对象。本质上是以空间换时间,不管用不用,先登月再说。
class AnimalManager
{
public:
static GetInterface();
private:
AnimalManager();
static AnimalManager m_animalManager;
};
具体实现
AnimalManager AnimalManager::m_animalManager;
AnimalManager* AnimalManager::getAnimalManager()
{
return m_animalManager;
}
类内开放接口的弊端
静态变量的初始化顺序不受声明顺序影响,受定义顺序影响,不同cpp中都有不同静态变量的定义,在我的理解是:其初始化顺序不确定。Animal.cpp中初始化静态变量s的时候调用AnimalManager的静态变量,由于静态变量的初始化顺序问题,AnimalManager中的m_animalManager还没有初始化,所以获取指针为空。解决方法就是将 AnimalManager AnimalManager::m_animalManager; 这个定义,放在初始化静态变量s之前。当然这个方法并不好。因为人为干预了初始化顺序,并且将AnimalManager的定义放在Animal的文件中也不合适。
最好的单例方式
不适用类内接口,用全局函数的获取静态单例类,通过局部变量的特性可以保证线程安全,而且不用锁。这种获取单例的方式也是我们实际工作中用到的。
AnimalManager* GetAnimalManager()
{
static AnimalManager s_instance;
return &s_instance;//静态方式得到管理员类
}