单例模式
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; // 初始化为 nullptr
std::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!
内存的释放过程如下图