初识设计模式1:单例模式(不考虑多线程)
初识设计模式1:单例模式
1、什么是单例模式?
在整个进程中,或者说是在整个工作过程中,只允许有一个实例对象的存在 。这个实例对象能够完成全部工作过程所需要的属性和方法。所以不需要再有其他的实例!这个单例模式往往运用在某些资源受限的场景 中,和操作系统的 “互斥信号量” 非常相似。比如打印机,我们限定只有一台打印机(资源受限),然后就得防止其他人另外开辟资源来实例化第二个打印机(可以理解为你们的资金只预算了这一台打印机,如果超预算,你得破产!)。并且这个资源不光是给一个人使用的,它是一个全局的访问点 !并且 一个类只能有这一个实例!
2、如何实现单例模式?
这个问题我们分解成几个小的问题来看:
2.1、如何保证一个唯一的实例?
实例如何而来?在面向对象编程中,对象实例全部来自构造方法,所以只需要隐藏构造方法 ,让其他用户无法去自行实例化即可。那么这个实例就会是属于这个类的一般实例 。既然这个实例是属于类的,那么说明这个实例是一个静态成员 !
2.2、如何获取这个唯一的实例?
只需要开放一个静态接口(因为实例也是静态的)。这个接口负责检查实例是否已经被创建,如若被创建就直接返回已经存在的实例,否则实例化一个给属于类的私有单例。
2.3、代码示例:
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* MySingleInstance;
Singleton() { cout << "单一实例已经被创建!" << endl; }
public:
static Singleton* GetMySingleInstance() {
if (nullptr == MySingleInstance)
MySingleInstance = new Singleton();
return MySingleInstance;
}
};
Singleton* Singleton::MySingleInstance = nullptr;
int main() {
Singleton* instance1 = Singleton::GetMySingleInstance();
Singleton* instance2 = Singleton::GetMySingleInstance();
if (instance1 == instance2)
cout << "这是一个单例模式!" << endl;
else
cout << "这不是一个单例模式!" << endl;
return 0;
}
2.4、结果展示:

2.5、做个小结:
这是一种典型的《懒汉模式》的实现方式,因为这个单例,是在第一次使用到的时候才去初始化的!如果是《饿汉模式》,则是在还没有需要使用单例的时候就已经完成初始化了!其实要想实现《饿汉模式》非常简单,只需要把我们的单例从一个对象指针变成一个静态的成员变量即可!如下:
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton MySingleInstance;
Singleton() { cout << "单一实例已经被创建!" << endl; }
public:
static Singleton* GetMySingleInstance() { return &MySingleInstance; }
};
Singleton Singleton::MySingleInstance;// 饿汉模式,这个类一存在这个单例就被创建!
int main() {
Singleton* instance1 = Singleton::GetMySingleInstance();
Singleton* instance2 = Singleton::GetMySingleInstance();
if (instance1 == instance2)
cout << "这是一个单例模式(饿汉模式)!" << endl;
else
cout << "这不是一个单例模式!" << endl;
return 0;
}
《饿汉模式》实现的单例模式运行结果如下:
那么饿汉模式和懒汉模式在具体应用上有何区别呢?查阅资料得知有线程安全方面的问题,但是我们这里只是初探,就不研究线程安全了!我个人理解来说,线程安全应该类似于《操作系统》里面互斥型信号量在同步访问的时候的线程保护吧,我没有仔细研究,如有说错的地方,还请各位高手指出 !
3、关于单例模式(懒汉)的资源回收问题
众所周知,懒汉模式下实现的单例,需要依赖动态内存分配。这种分配在堆空间的资源,一定是需要程序员通过代码来进行资源回收的!众所周知,只有尽量完美做到RAII原则的c++程序员才是一个优秀的c++程序员!我们该如何处理进程结束后的单例资源呢?
3.1、使用析构函数,自行delete释放
错误代码如下:
class Singleton {
private:
static Singleton* MySingleInstance;
Singleton() { cout << "单一实例被创建!" << endl; }
public:
static Singleton* GetMySingleInstance() {
if (nullptr == MySingleInstance)
MySingleInstance = new Singleton();
return MySingleInstance;
}
~Singleton() {
delete Singleton::MySingleInstance;
// delete 之后调用析构函数,然后析构函数又调用 delete … 循环往复!
cout << "单例资源被释放" << endl;
}
};
Singleton* Singleton::MySingleInstance = nullptr;// 饿汉模式,这个类一存在这个单例就被创建!
int main() {
Singleton* instance = Singleton::GetMySingleInstance();
delete instance;
return 0;
}
这种操作无法完成析构,甚至还会陷入死循环带来程序崩溃 !原因我在上面的注释里写到了:delete 之后调用析构函数,然后析构函数又调用 delete … 循环往复!
那是否这种调用析构函数的方式不可行呢?其实不是的!
如何避免这个已经被delete后的对象,再次进入属于它的析构方法呢?只需要进行一次标记就好:
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* MySingleInstance;
Singleton() { cout << "单一实例被创建!" << endl; }
public:
static Singleton* GetMySingleInstance() {
if (nullptr == MySingleInstance)
MySingleInstance = new Singleton();
return MySingleInstance;
}
~Singleton() {
if (nullptr != MySingleInstance) {// 检测标记
Singleton::MySingleInstance = nullptr;// 做上标记
delete Singleton::MySingleInstance;
}
cout << "单例资源被释放" << endl;
}
};
Singleton* Singleton::MySingleInstance = nullptr;// 饿汉模式,这个类一存在这个单例就被创建!
int main() {
Singleton* instance = Singleton::GetMySingleInstance();
delete instance;
return 0;
}
析构后效果展示:

3.2、能否让系统自行在进程结束之后,完成单例资源的回收呢?
众所周知,静态对象,在发生类创建的进程 结束后,会系统自动回收资源,这个资源的回收也是调用了静态对象的析构方法!于是我们可以利用这个特点,让一个单例伴随一个静态对象,如何才能实现这点呢?只需要写一个内部类,创建一个内部类对象,这个对象是和成员属性、成员方法同级别的,是属于外部类的实例的!也就是属于单例的一个伴随对象!我们只需要让这个对象成为一个静态对象,那么就可以在实例工作结束后,在它的析构中完成对实例的析构!
系统自动回收的代码框架:
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* MySingleInstance;
Singleton() { cout << "单一实例被创建!" << endl; }
class GarbageCollector {
public:
~GarbageCollector() {
if (nullptr != Singleton::MySingleInstance) {
Singleton::MySingleInstance = nullptr;// 做上标记
delete Singleton::MySingleInstance;
}
cout << "单例资源被释放" << endl;
}
};
static GarbageCollector MyGC;
public:
static Singleton* GetMySingleInstance() {
if (nullptr == MySingleInstance)
MySingleInstance = new Singleton();
return MySingleInstance;
}
};
Singleton* Singleton::MySingleInstance = nullptr;// 饿汉模式,这个类一存在这个单例就被创建!
Singleton::GarbageCollector Singleton::MyGC;
int main() {
Singleton* instance = Singleton::GetMySingleInstance();
return 0;
}
系统自动回收单例资源的结果展示:

4、一个具体的应用场景:
假设需要对一个单例资源:打印机进行处于单例模式下的工作,我的代码如下:
#include <iostream>
using namespace std;
class printing {
public:
static printing* GetPrintingInstance() {
if (nullptr == MyInstance)
MyInstance = new printing();
return MyInstance;
}
class Garbo {
public:
~Garbo(){
if (nullptr != printing::MyInstance) {
printing::MyInstance = nullptr;
delete printing::MyInstance;
cout << "打印机实例注销" << endl;
}
}
};
static Garbo MyGarbo;
static int number;
void doPrint() {
cout << "打印机正在打印第:" << number << " 个打印任务" << endl;
number++;
}
private:
printing() { cout << "打印机实例启动" << endl; }
static printing* MyInstance;
};
int printing::number = 1;
printing* printing::MyInstance = nullptr;
printing::Garbo MyGarbo;// 默认构造,初始化 MyGarbo
void doIt() {
printing* util = printing::GetPrintingInstance();
int task = 20;
while (task--)
util->doPrint();
}
int main() {
doIt();
return 0;
}
应用结果展示如下:

写在后面的话:
本人也没有在课堂上学过《设计模式》,也是在课余时间里做项目遇到了。深感设计模型、面向对象思想,在大型软件设计中的作用!他们都能大大提高代码的可复用性,减小工作量、减小代码冗余,增加软件的稳定性!由于没有正式学习设计模式,于是本文标题也只敢写:《初始设计模式》,写的不对或者不好的地方,还请各位前辈大佬提出,欢迎指正!!!!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)