设计模式之单例模式

一、设计模式
    设计模式代表了最佳实践,是软件开发过程中面临一般问题的解决方案
    设计模式是一套被反复使用、经过分类、代码设计总结的经验
二、单例模式
    单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用到它,所以构建一个线程安全并且高效的Singleton很重要
   1. 单例类保证全局只有一个唯一实例对象。

   2. 单例类提供获取这个唯一实例的接口。


以下先介绍下面试问的比较多的两个题:

    1.只能在栈上生成的对象的类

    2。只能在堆上生成对象的类

//只能在栈上生成对象的类
class Singleton
{
public:
<span style="white-space:pre">	</span>statci Singleton* GetSingletong()
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>if(_sInstance == NULL)
<span style="white-space:pre">			</span>_sInstance = new Singleton;
<span style="white-space:pre">		</span>return _sInstance;
<span style="white-space:pre">	</span>}
private:
<span style="white-space:pre">	</span>int _a;
<span style="white-space:pre">	</span>static Singleton* _sInstance;
<span style="white-space:pre">	</span>Singletong()
<span style="white-space:pre">	</span>:_a(5)
<span style="white-space:pre">	</span>{}

<span style="white-space:pre">	</span>Singletong(const Singleton&);
<span style="white-space:pre">	</span>Singletong& operator=(const Singleton&);
};

</pre><pre name="code" class="html">//只能在栈上生成对象的单例模式
class Singleton
{
public:
<span style="white-space:pre">	</span>static Singletong* GetSingletong()
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>static Singletong s;
<span style="white-space:pre">		</span>return &s;
<span style="white-space:pre">	</span>}
private:
<span style="white-space:pre">	</span>int _a;
<span style="white-space:pre">	</span>Singletong()
<span style="white-space:pre">	</span>:_a(5)
<span style="white-space:pre">	</span>{}

<span style="white-space:pre">	</span>Singleton(const Singletong&);
<span style="white-space:pre">	</span>Singletong& operator=(const Singletong&);
}


    上面两个的区分就是一个是用了栈空间,一个用了堆空间,两个都是单列模式,但是要实现一个既高效又是线程安全的单例模式,上面的两种都还达不到标准。下面从三个方面来实现这个神秘的单例模式

第一、为了实现线程安全,加锁!!!

第二、因为每次获取单例对象都需要做加锁解锁操作,而实际上只有第一次产生单例模式需要这么做,所以使用双重判断来提高效率。

第三、为了防止系统优化进行指令重排,使用栅栏,防止得到的对象为随机值。

就具体实现如下:

//高效、线程安全的单例模式
#include <mutex>
#include <Windows.h>
<pre name="code" class="cpp" style="font-size: 16px;">class Singleton
{
public:
<span>	</span>statci Singleton* GetSingletong()
<span>	</span>{
<span style="white-space:pre">		</span>if(_sInstance == NULL)//双重判断提高效率
<span style="white-space:pre">		</span>{
<span style="white-space:pre">			</span>lock_guard<mutex> lck(_mtx);//加锁线程安全
<span>			</span>if(_sInstance == NULL)
<span style="white-space:pre">			</span>{
<span style="white-space:pre">				</span>Singletong* tmp = new Singletong;
<span style="white-space:pre">				</span>memoryBarrier();//使用栅栏防止编译器优化使,使指令重排
</pre><pre name="code" class="cpp" style="font-size: 16px;"><span style="white-space:pre">				<span style="font-size: 12pt; color: rgb(0, 176, 240);">//<span style="font-size: 12pt;">1.分配空间 2.调用构造函数 3.赋值
<span style="font-size: 12pt;"><span style="white-space:pre">				</span>//<span style="font-size: 12pt;">编译器编译优化可能会把2<span style="font-size: 12pt;">和3进行指令重排,这样可能会导致
<span style="font-size: 12pt;"><span style="white-space:pre">				</span>//<span style="font-size: 12pt;">高并发场<span style="font-size: 12pt;">景下<span style="font-size: 12pt;">,其他线程获取到未调用构造函数初始化的对象
<span style="font-size: 12pt;"><span style="white-space:pre">				</span>//<span style="font-size: 12pt;">以下加入<span style="font-size: 12pt;">内存栅栏进行处理,防止编译器重排栅栏后面的赋值
<span style="font-size: 12pt;"><span style="white-space:pre">				</span>//<span style="font-size: 12pt;">到内存栅栏之前</span></span></span></span></span></span></span></span></span></span></span></span></span><br style="text-align: -webkit-auto;" /></span>
	</span><pre name="code" class="cpp" style="font-size: 16px;"><span style="white-space:pre">				</span>_sInstance = tmp;

 
<span style="white-space:pre">			</span>}
<span>				</span>
<span style="white-space:pre">		</span>}
<span>		</span>return _sInstance;
<span>	</span>}
private:
<span>	</span>int _a;
<span>	</span>static Singleton* _sInstance;
<span style="white-space:pre">	</span>static mutex _mtx;//互斥锁<span style="white-space:pre">	</span>
<span>	</span>Singletong()
<span>	</span>:_a(5)
<span>	</span>{}
</pre><pre name="code" class="cpp" style="font-size: 16px;"><span>	</span>Singletong(const Singleton&);
<span>	</span>Singletong& operator=(const Singleton&);
};

 

上面实现的模式叫做懒汉模式,下面讲解下饿汉模式的单例模式

线程安全(饿汉模式--简洁、高效、不用加锁、但是在某些场景下会有缺陷)
//方式一
class Singleton
{public:
//获取唯一对象实例的接口函数
static Singleton* GetInstance()
{
<span style="white-space:pre">	</span>static Singleton sInstance;
<span style="white-space:pre">	</span>return &sInstance;
} 
void Print()
{
<span style="white-space:pre">	</span>cout<<_data<<endl;
}
private:
//构造函数定义为私有,限制只能在类内创建对象
Singleton()
:_data(0)
{}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
//单例类里面的数据
int _data;
};
void TestSingleton()
{
<span style="white-space:pre">	</span>Singleton::GetInstance()->Print();
} //
方式二
class Singleton
{
public:
//获取唯一对象实例的接口函数
static Singleton* GetInstance()
{
<span style="white-space:pre">	</span>assert(_sInstance);
<span style="white-space:pre">	</span>return _sInstance;
} 
//删除实例对象
static void DelInstance()
{
<span style="white-space:pre">	</span>if (_sInstance)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>delete _sInstance;
<span style="white-space:pre">		</span>_sInstance = NULL;
<span style="white-space:pre">	</span>}
} 
void Print()
{
<span style="white-space:pre">	</span>cout << _data << endl;
}
private:
//构造函数定义为私有,限制只能在类内创建对象
Singleton()
:_data(0)
{}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
static Singleton* _sInstance;
//单例类里面的数据
int _data;
};

Singleton* Singleton::_sInstance = new Singleton;

void TestSingleton()
{
<span style="white-space:pre">	</span>Singleton::GetInstance()->Print();
<span style="white-space:pre">	</span>Singleton::DelInstance();
}

带RAII GC 自动回收实例对象的方式
class Singleton
{

public:
//获取唯一对象实例的接口函数
static Singleton* GetInstance()
{
assert(_sInstance);
return _sInstance;

//删除实例对象
static void DelInstance()
{
if (_sInstance)
{
delete _sInstance;
_sInstance = NULL;
}

void Print()
{
cout << _data << endl;

class GC
{

public:
~GC()
{
cout << "DelInstance()"<<endl;
DelInstance();
}
};
private:
//构造函数定义为私有,限制只能在类内创建对象
Singleton()
:_data(0)
{}
//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
static Singleton* _sInstance;
//单例类里面的数据
int _data;
};
//静态对象在main函数之前初始化,这时只有主线程运行,所以是线程安全的。
Singleton* Singleton::_sInstance = new Singleton;
//使用RAII,定义全局的GC对象释放对象实例Singleton::GC gc;
void TestSingleton()
{
Singleton::GetInstance()->Print();
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值