设计模式之单例模式
1. 单例模式
1.1 简介
在实际项目开发中肯定会有这么一种情况,一个类只能有一个对象被创建,如果有多个对象的话, 可能会导致状态的混乱和不一致。这种情况下,单例模式是最恰当的解决办法。 它有很多种实现方式,各自的特性不相同,使用的情形也不相同。
1.2 特点
1.提供了一个对对象的全局访问指针
2.在不影响单例类的客户端的情况下允许将来有多个实例,通过接口获得
3.确保一个类只有一个实例被建立 ,不能额外创建一个新的对象
1.3 应用场景
-
读取配置文件,读取的配置文件都是公用的,只能有一个示例去操作文件
-
数据连接线程池,多线程的时候.
-
应用程序的日志应用,一般都可以使用单例模式来实现,只能又一个示例去操作文件
1.4 实现思路
1)将构造函数的访问权限修改为private或protected,不允许类的外部来创建对象
2)用一个静态成员指针指向创建的对象
3)提供一个静态成员函数,获得这个对象的首地址
2. 懒汉式
先上代码:
#include <iostream>
using namespace std;
// 作者博客
static string myblog = "https://www.cnblogs.com/wanghongyang/";
class singelton {
private:
singelton() { ; }
static singelton *single;
public:
static singelton *get_instance();
};
// 静态变量初始化
singelton* singelton::single = NULL;
singelton * singelton::get_instance()
{
if (single == NULL) {
single = new singelton;
}
return single;
}
int main()
{
singelton* s1 = singelton::get_instance();
singelton* s2 = singelton::get_instance();
if (s1 == s2)
{
cout << "s1==s2" << endl;
}
else {
cout << "s1!=s2" << endl;
}
return 0;
}
可以看到,思路是非常简单的,所谓懒汉式,就是当你用的时候,我才初始化
但是这会带来一个问题,大家思考一下,如果添加了析构函数将会怎么样,如何解决?
参考代码如下
class singelton {
private:
singelton() { ; }
static singelton *single;
public:
static singelton *get_instance();
~singelton() {
cout << "~singelton()" << endl;
if (single != NULL)
{
delete single;
}
}
};
// 静态变量初始化
singelton* singelton::single = NULL;
singelton * singelton::get_instance()
{
if (single == NULL) {
single = new singelton;
}
return single;
}
int main()
{
singelton* s1 = singelton::get_instance();
singelton* s2 = singelton::get_instance();
if (s1 == s2)
{
cout << "s1==s2" << endl;
}
else {
cout << "s1!=s2" << endl;
}
delete s1;
return 0;
}
可以看到,会出现不断调用析构函数的现象,因为singelton为用户自定义的类,delete会默认调用该类的析构函数
运行结果:
2.1 解决方案
自定义一个类,用于释放空间
class singelton {
private:
singelton() { ; }
static singelton *single;
class DeleteSpace {
public:
DeleteSpace();
~DeleteSpace();
};
static DeleteSpace ds; // 释放堆区空间的类对象
public:
static singelton *get_instance();
};
// 静态变量初始化
singelton* singelton::single = NULL;
singelton * singelton::get_instance()
{
if (single == NULL) {
single = new singelton;
}
return single;
}
singelton::DeleteSpace singelton::ds; // 初始化释放空间的静态类
singelton::DeleteSpace::DeleteSpace() { ; }
singelton::DeleteSpace::~DeleteSpace() {
cout << "~DeleteSpace()" << endl;
if (single != NULL)
{
delete single;
}
}
int main()
{
singelton* s1 = singelton::get_instance();
singelton* s2 = singelton::get_instance();
if (s1 == s2)
{
cout << "s1==s2" << endl;
}
else {
cout << "s1!=s2" << endl;
}
return 0;
}
这个例子的原理就是,当程序运行结束,堆区释放的时候,会自动调用析构函数,析构单例类,这样做就不需要手动的去释放单例类
运行结果
2.2 优缺点
优点:
在使用的时候,才会分配内存,创建对象
缺点:
在多线程使用,会出现创建多个对象的问题,线程不安全
2.3 线程安全示例代码
在刚刚的那种懒汉式的方法中,如果有多线程的情况,可能会大家都以为自己是第一个创建单例类,因此有可能创建多个单例类,为了保证线程安全,下面演示线程不安全的代码
结果可以看到,调用了三次构造函数
解决办法
添加互斥锁来解决
3. 饿汉式
在单例类定义的时候就进行了实例化。---在类定义的时候,分配空间
3.1 优缺点
优点: 线程安全
缺点:还没有正式使用,就分配了内存空间,浪费了内存