简介
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。
应用
一些资源管理器需要设计成单例模式。
- 当使用打印机时,打印机一次只能处理一个打印任务。另一个访问任务(另一个实例)被拒绝访问。
- 传真机一次只能传送一份传真作业。另一份传真作业(另一个实例)被拒绝传送。
这些资源管理需要满足的条件
- 资源管理器构件必须只有一个实例。
- 它们必须自行初始化。
- 允许整个系统访问自己。
要点
某个类只能有一个实例
它必须自行创建这个实例
它必须自行向整个系统提供这个实例
实现
单例模式的类只提供私有的构造函数。构造函数若定义为公有,则任意对象均可实例化。
类定义中含有一个该类的静态私有对象。静态变量只初始化一次,在类外初始化。静态数据成员只有一个复制品。
类提供了一个静态公有函数用于创建或获取它本身的静态私有对象。静态成员函数属于整个类,通过类名::静态公有函数访问。
注意
构造函数可设置为受保护权限以允许子类派生。
单例模式只考虑对象创建的管理,没有考虑对象销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。但我们需要注意资源回收问题。
单例模式的三种形式
懒汉式
饿汉式
双重锁形式
懒汉式
在使用时初始化,存在多线程安全问题。
并发线程的次序会使过程受到影响。
class S
{
private:
S() {} // 私有的构造函数
public:
static S* instrance(); //静态成员函数,提供访问接口和实例化对象
static void destory(); //不再需要该实例时进行资源释放
private:
static S *s; // 对象指针,存在内存泄漏问题
};
S *S::s = nullptr; //先初始化为nullptr,使用时实例化
S* S::instrance() {
if (s == nullptr)
s = new S();
return s;
}
void S::destory() {
if (s != nullptr) {
delete s;
s = nullptr;
}
}
饿汉式
- 直接实例化,多线程使用时处于安全状态。
class S
{
private:
S() {} // 私有的构造函数
public:
static S* instrance(); //静态成员函数,提供访问接口和实例化对象
static void destory(); //不再需要该实例时,需要显式进行资源释放
private:
static S *s; // 对象指针,存在内存泄漏问题
};
S *S::s = new S(); //直接实例化
S* S::instrance() {
return s;
}
void S::destory() {
if (s != nullptr) {
delete s;
s = nullptr;
}
}
双重锁形式
- 双重检查锁DCL,主要防护懒汉式的线程安全问题。
class S
{
private:
S() {} // 私有的构造函数
public:
static S* instrance(); //静态成员函数,提供访问接口和实例化对象
static void destory(); //不再需要该实例时进行资源释放
private:
static S *s; // 对象指针,存在内存泄漏问题
static mutex m; // 双重检查锁DCL
};
S *S::s = nullptr; //先初始化为nullptr,使用时实例化
mutex S::m;
S* S::instrance() {
lock_guard<mutex> lock(m);
if (s == nullptr)
s = new S();
return s;
}
void S::destory() {
if (s != nullptr) {
delete s;
s = nullptr;
}
}
显示释放
我们定义了静态的成员函数进行显式的资源释放。具体为:当不再需要该实例时,调用该静态成员函数,销毁对象。
存在问题:忘记销毁???
void S::destory() {
if (s != nullptr) {
delete s;
s = nullptr;
}
}
隐式
class S
{
private:
S() {} // 私有的构造函数
public:
static S* instrance(); //静态成员函数,提供访问接口和实例化对象
private:
static S *s; // 对象指针,存在内存泄漏问题
static mutex m; // 双重检查锁DCL
class Garbage_Collection {
~Garbage_Collection()
{
if (s != nullptr) {
delete s;
s = nullptr;
}
}
static Garbage_Collection g;
};
};
S *S::s = nullptr; //先初始化为nullptr,使用时实例化
mutex S::m;
S::Garbage_Collection S::Garbage_Collection::g; //静态变量会在程序结束时释放,静态对象会调用析构
S* S::instrance() {
lock_guard<mutex> lock(m);
if (s == nullptr)
s = new S();
return s;
}