Chapter3—单例模式
单例模式
1 概念
单例模式是指软件设计中当前系统中只有一个实例对象,同时提供集中、统一的访问接口,以使系统行为保持协调一致。
2 意图(Intent)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
3 动机(Motivate)
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?这应该是类设计者的责任,而不是类使用者的责任。
4 类图结构
5 角色定义
- Singleton(单例类):包含一个自己的类实例的属性,并把构造方法用private关键字隐藏起来,只对外提供GetInstance()方法以获得这个单例对象。
6 程序示例
饿汉式:在调用之初就创建
/*** @brief 饿汉式单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{return m_spInstance;
}void Singleton::DestroyInstance()
{if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = new Singleton(); // 静态创建,系统初始化的时候自动就创建了/*** @brief 饿汉式单例测试函数*/
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}
懒汉式:在调用之后再创建
/*** @brief 懒汉式单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{if (nullptr == m_spInstance)m_spInstance = new Singleton(); // 在第一次调用的时候才创建return m_spInstance;
}void Singleton::DestroyInstance()
{if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = nullptr; // 初始化为 nullptr/*** @brief 懒汉式单例测试函数*/
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}
局部静态变量方式
此方式代码简洁且高效,这种好处就是不用管理内存,局部静态变量会自动析构,对于饿汉式和懒汉式都需要去手动释放下内存。
/*** @brief 局部静态变量方式的单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{static Singleton instance;return &instance;
}/*** @brief 单例测试函数,不需要主动调用释放内存了*/
void TestSingleton()
{Singleton::GetInstance()->Show();
}
懒汉式单例线程安全
#include <mutex>using std::string;
using std::vector;
using std::mutex;/*** @brief 懒汉式单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;static std::mutex m_sMutex; // 增加锁
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{// 双重检查锁定是一种高效的线程安全实现方式,通过减少锁的使用频率来提升性能。// 如果直接加在最外面,每调用一次就得进行一次加锁,这样效率十分低下if (nullptr == m_spInstance){std::lock_guard<std::mutex> lockGuard(m_sMutex);if (nullptr == m_spInstance)m_spInstance = new Singleton();}return m_spInstance;
}void Singleton::DestroyInstance()
{// 析构函数不会经常调用,所以直接在最外层加锁即可std::lock_guard<std::mutex> lockGuard(m_sMutex);if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = nullptr; // 初始化为 nullptrstd::mutex Singleton::m_sMutex = std::mutex();/*** @brief 单例测试函数*/
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}
模板类单例
单例模板类:
#include <mutex>using std::string;
using std::vector;
using std::mutex;template <typename T>
class Singleton
{
public:template <typename... Args>static void InitInstance(Args... args);static T* GetInstance();static void DestroyInstance();private:static T* sm_pInstance;static mutex sm_Mutex;private:Singleton() = default;~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;
};template <typename T>
T* Singleton<T>::sm_pInstance = nullptr;template <typename T>
mutex Singleton<T>::sm_Mutex = mutex();template<typename T>
template<typename... Args>
void Singleton<T>::InitInstance(Args... args)
{if (nullptr == sm_pInstance){std::lock_guard<mutex> lockGuard(sm_Mutex);if (nullptr == sm_pInstance)sm_pInstance = new T(std::forward<Args>(args)...); // 参数完美转发}
}template<typename T>
T *Singleton<T>::GetInstance()
{return sm_pInstance;
}template<typename T>
void Singleton<T>::DestroyInstance()
{std::lock_guard<mutex> lockGuard(sm_Mutex);if (nullptr != sm_pInstance){delete sm_pInstance;sm_pInstance = nullptr;}
}
一般类:
class Person
{
public:Person(const string& strName, int nAge);~Person();public:void Show();private:string m_strName;int m_nAge;
};Person::Person(const string &strName, int nAge):m_strName(strName), m_nAge(nAge)
{qDebug() << "Create Perosn";
}Person::~Person()
{qDebug() << "Release Perosn";
}void Person::Show()
{qDebug() << "[Name =" << m_strName.c_str() << ", Age =" << m_nAge << "]";
}
采用模板单例类框架生成一个一般类的单例类:
void TestSingleton()
{Singleton<Person>::InitInstance("ZhangSan", 23);Singleton<Person>::GetInstance()->Show();Singleton<Person>::DestroyInstance();
}
7 思考小结
当系统中某个类型仅需要一个对象时候可以考虑使用单例模式。在C++开发中,我们也通常建议使用局部静态变量的方式,也就是第三种方式。单例模式一直是一个有争议的设计模式,因为其本身不存在抽象的概念,没有虚函数(接口),系统难以进行扩展,所以有些人甚至建议将单例模式剔除出设计模式的范畴。
8 附录
如何实现饿汉式单例的内存自动释放?思路有点类似于C++的RAII,我们拿一个身份证类的单例来实现,程序代码如下
class IdentityCard
{
private:struct AutoRelease // 内部类,负责单例对象自动析构,在析构函数中释放单例对象{AutoRelease(){}~AutoRelease(){if (nullptr != sm_pInstance){delete sm_pInstance;sm_pInstance = nullptr;}}};private:IdentityCard();public:~IdentityCard();public:IdentityCard(const IdentityCard& src) = delete;IdentityCard(IdentityCard&& src) = delete;IdentityCard& operator=(const IdentityCard& src) = delete;IdentityCard& operator=(IdentityCard&& src) = delete;public:static IdentityCard* GetInstance();private:static IdentityCard* sm_pInstance;static AutoRelease sm_AutoRelease; // 声明为静态成员变量,在程序结束时会自动调用析构函数,从而释放掉单例的对象public:void SetNumber(const string& strNumber);string GetNumber();string m_strNumber;
};IdentityCard* IdentityCard::sm_pInstance = new IdentityCard();IdentityCard::AutoRelease IdentityCard::sm_AutoRelease = IdentityCard::AutoRelease();IdentityCard::IdentityCard()
{qDebug() << "Create IdentityCard!";
}IdentityCard::~IdentityCard()
{qDebug() << "Release IdentityCard!";
}IdentityCard *IdentityCard::GetInstance()
{return sm_pInstance;
}void IdentityCard::SetNumber(const string &strNumber)
{m_strNumber = strNumber;
}string IdentityCard::GetNumber()
{return m_strNumber;
}
自动释放内存的单例测试函数
void TestIdentityCard()
{IdentityCard::GetInstance()->SetNumber("ACB");qDebug() << IdentityCard::GetInstance()->GetNumber().c_str();
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);TestIdentityCard();return 0;
}
输出结果
Create IdentityCard!
ACB
Release IdentityCard!
内存的释放过程如下图