###单例模式
单例模式是一种常见的设计模式
表示的含义是某一个类,在一个进程中只有唯一的一个对象,并且在语法角度上进行制约
###为什么要有单例模式 在系统中,有些类必须保证只能创造出一个对象 我们windows系统中,只能打开一个资源管理器,因为若是可以打开多个资源管理器窗口,就会存在这样的问题: 1.若这些窗口中显示的内容是均相同的,这是一种资源的浪费 2.若这些窗口中显示的内容是不相同的,那么到底哪一个才是正真反应出当前的各个任务状态呢?或者说同一时刻存在两种不同的状态,这怕是与实际不相符吧
###单例模式要点
- 一个类只能实例化出一个对象
- 必须由自己进行创建
- 它必须向整个系统提供访问接口
###单例模式的实现
(方案一)饿汉模式
对象定义后数据立即加载到内存,以空间换时间的做法
class Singleton_1
{
public:
static Singleton_1 *GetInstance()//静态的成员函数,为外部提供接口
{
return _inst;
}
protected:
Singleton_1() {};
private:
Singleton_1(const Singleton_1 & ) = delete;
Singleton_1 operator = (const Singleton_1 &) = delete;
static Singleton_1 *_inst;//静态数据员。在该类范围中只能定义一个
};
//需要先对静态成员进行初始化
Singleton_1 *Singleton_1:: _inst = new Singleton_1();
//得到唯一的对象
Singleton_1 * inst_ptr = Singleton_1::GetInstance();
举例验证
#include <stdio.h>
#include <iostream>
using namespace std;
template <class T>
//类模板
class Singleton_1
{
public:
static T *GetInstance()//静态的成员函数,用于访问 T 数据
{
return _inst;
}
private:
Singleton_1(const T & ) = delete;
Singleton_1 & operator = (const T &) = delete;
static T* _inst;//静态数据员。在该类范围中只能定义一个
protected:
Singleton_1() {};
//构造函数设为保护的是为了继承下去
//这里采用静态成员意为 定义多个Signleton对象只能定义出一个T对象
};
//需要先对静态成员进行初始化
template<class T>
T *Singleton_1<T>:: _inst = new T();
//规定 T 类对象只能通过 Signleton对象定义出来
//获得唯一的对象
int main()
{
int * ret_ptr_1 = Singleton_1<int>::GetInstance();
int * ret_ptr_2 = Singleton_1<int>::GetInstance();
*ret_ptr_2 = 9;
*ret_ptr_1 = 8;
printf("ret_1 :%d\n",*ret_ptr_1);
printf("ret_2 :%d\n",*ret_ptr_2);
printf("ret_ptr_1 :%p\n",ret_ptr_1);
printf("ret_ptr_2 :%p\n",ret_ptr_2);
return 0;
}
结果显示:
ret_1 :8
ret_2 :8
ret_ptr_1 :0x12c7c20
ret_ptr_2 :0x12c7c20
饿汉模式是一种线程安全的模式
(方案二)懒汉模式
对象定义出来 先不着急加载到内存,等到第一次使用的时候,再将数据加载到内存,以时间换空间的做法,其实也是一种延时加载
class Singleton_2
{
public:
static Singleton_2 *GetInstance()
{
if(_inst == NULL)
{//定义的时候先没有开辟空间,只有在用的时候进行调用
//多次调用的时候,若不为空可以直接返回
_inst = new Singleton_2();
}
return _inst;
};
protected:
Singleton_2(){};
private:
static Singleton_2 * _inst;
Singleton_2(const Singleton_2 &) = delete; //防止拷贝
Singleton_2 operator = (const Singleton_2 &) = delete; //防止赋值
};
//静态成员需要先进行初始化为NULL
Singleton_2 * Singleton_2::_inst = NULL;
Singleton_2 * inst_ptr= Singleton_2::GetInstance();
##注意
- 这种懒汉单例模式并不是一个线程安全模式,因为在判断_inst是否为空和开辟空间并不是原子操作
- 若是两个线程都执行到判断这一步,就会创建两个对象,就会出现逻辑错误
- 所以要在判断和创建对象时加上互斥锁
- 有为了效率问题,我们只需要在_inst为空的时候再进行加锁
- 又为了防止编译器过度优化,我们确保每次判断的时候值都是从内存中读取的,我们需要对私有成员加上 volatile关键字
(方案三)线程安全的懒汉模式
//线程安全的机制的懒汉模式
class Singleton_3
{
public:
static pthread_mutex_t lock;
volatile static Singleton_3 * GetInstance()
{
if(_inst == NULL)
{
//每次先判断为空,即还有没开辟空间的时候,在实施线程安全的措施
//否则的话,每一次调用函数的的时候都判断就太浪费资源了
pthread_mutex_lock(&lock);
if(_inst == NULL)
{
_inst = new Singleton_3();
}
pthread_mutex_unlock(&lock);
}
return _inst;
}
protected:
//在构造函数种对互斥锁进行初始化
Singleton_3()
{
pthread_mutex_init(&lock,NULL);
};
private:
volatile static Singleton_3 * _inst;
//防止编译器进行优化
Singleton_3(const Singleton_3 &) = delete;
Singleton_3 * operator =(const Singleton_3 &) = delete;
};
//对静态成员的初始化
pthread_mutex_t Singleton_3:: lock ;
volatile Singleton_3 * Singleton_3:: _inst = NULL;
int main()
{
volatile Singleton_3 * ret_ptr_1 = Singleton_3 ::GetInstance();
volatile Singleton_3 * ret_ptr_2 = Singleton_3 ::GetInstance();
volatile Singleton_3 * ret_ptr_3 = Singleton_3 ::GetInstance();
printf("ret_ptr_1 :%p\n",ret_ptr_1);
printf("ret_ptr_2 :%p\n",ret_ptr_2);
printf("ret_ptr_3 :%p\n",ret_ptr_3);
return 0;
}
结果:
ret_ptr_1 :0xbadc20
ret_ptr_2 :0xbadc20
ret_ptr_3 :0xbadc20