三、道阻且长之单例模式
单例模式

首先什么是单例模式:
 《设计模式》一书中给出的定义是:让类自身负责保存它的唯一实例,这个类可以保证没有其他实例被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法,这就是Singleton模式。有人叫单例模式,也有叫单件模式。那么如何实现以上要求呢?首先,需要保证一个类只有一个实例;在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。
####单例大约有两种实现方法:懒汉与饿汉。
 懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化;
 饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
 ###特点与选择:
 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
 在访问量较小时,采用懒汉实现。这是以时间换空间。
 ###C++代码实现
- 经典版:
 
#include <iostream>
using namespace std;
 //懒汉模式
class Singleton
{
public:
/**
*需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例
*/
	static Singleton *GetInstance()
	{
		if (m_Instance == NULL )
		{ m_Instance = new Singleton ();
		}
		return m_Instance;
	} static void DestoryInstance()
	{
		if (m_Instance != NULL )
		{ delete m_Instance; m_Instance = NULL ;
		}
	}
private:
/**
*构造函数卸载私有里,为了防止在外部调用类的构造函数而构造实例
*/
	Singleton();
	static Singleton *m_Instance;
};
 
Singleton *Singleton ::m_Instance = NULL;
 
int main(int argc , char *argv [])
{
	Singleton *singletonObj = Singleton ::GetInstance(); 
	Singleton ::DestoryInstance();
	return 0;
}
  
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 
这是最简单,也是最普遍的实现方式,也是现在网上各个博客中记述的实现方式,但是,这种实现方式,有很多问题,比如:没有考虑到多线程的问题,在多线程的情况下,就可能创建多个Singleton实例,以下版本是改善的版本。
- 经典版优化:-
 
#include <iostream>
using namespace std;
 //懒汉模式
class Singleton
{
public:
/*
此处进行了两次m_Instance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。
因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也
保证了线程安全。但是,这种实现方法在平时的项目开发中用的很好,也没有什么问题?但是,如果进行大数据
的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。
*/
	static Singleton *GetInstance()
	{
		if (m_Instance == NULL )
		{ Lock(); if (m_Instance == NULL ) { m_Instance = new Singleton (); } UnLock(); }
		return m_Instance;
	} static void DestoryInstance()
	{
		if (m_Instance != NULL )
		{ delete m_Instance; m_Instance = NULL ;
		}
	}
private:
	Singleton();
	static Singleton *m_Instance;
};
 
Singleton *Singleton ::m_Instance = NULL;
 
int main(int argc , char *argv [])
{
	Singleton *singletonObj = Singleton ::GetInstance();
	Singleton ::DestoryInstance();
	return 0;
}
  
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 
- 内部静态变量的懒汉实现:-
此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。 
#include <iostream>
using namespace std;
 
class Singleton
{
public:
	static Singleton *GetInstance()
	{
		lock();
		static Singleton m_Instance;
		unlock();
		return &m_Instance;
	} 
private:
	Singleton();
};
 
int main(int argc , char *argv [])
{
	Singleton *singletonObj = Singleton ::GetInstance();
	cout<<singletonObj->GetTest()<<endl; singletonObj = Singleton ::GetInstance();
	cout<<singletonObj->GetTest()<<endl;
}
  
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 
因为static Singleton *GetInstance()是不可重入函数,所以存在线程安全问题,访问静态变量时要加锁;那么一个函数要成为可重入函数,必须要具备以下几个特点:
 1、仅依赖于调用方传入的参数
 2、不依赖于任何单个资源的锁
 3、不反回任何(局部)静态或全局的非const变量的指针
 4、不使用任何(局部)静态或全局的非const变量
 5、不调用任何不可重入函数
 可重入是并发安全的强力保证,一个可重入函数可以在多线程环境下放心使用。
- 饿汉实现:-
 
#include <iostream>
using namespace std;
 
class Singleton
{
public:
	static Singleton *GetInstance()
	{
		return m_instace;
	} 
private:
	Singleton();
	static Singleton *m_instance;
};
Singleton* Singleton :: m_instance = new Singleton();
int main(int argc , char *argv [])
{
	Singleton *singletonObj = Singleton ::GetInstance();
	singletonObj = Singleton ::GetInstance();
	return 0;
}
  
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 
因为静态初始化在程序开始时,也就是进入主函数之前,由主线程以单线程方式完成了初始化,所以静态初始化实例保证了线程安全性。在性能要求比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁造成的资源浪费。
- 实例销毁:-
 
#include <iostream>
using namespace std;
 
class Singleton
{
public:
	static Singleton *GetInstance()
	{
		return m_Instance;
	} int GetTest()
	{
		return m_Test;
	}
 
private:
	Singleton(){ m_Test = 10; }
	static Singleton *m_Instance;
	int m_Test; // This is important
	class GC
	{
	public :
		~GC()
		{ // We can destory all the resouce here, eg:db connector, file handle and so on if (m_Instance != NULL ) { cout<< "Here is the test" <<endl; delete m_Instance; m_Instance = NULL ; }
		}
	};
	static GC gc;
};
 
Singleton *Singleton ::m_Instance = new Singleton();
Singleton ::GC Singleton ::gc;
 
int main(int argc , char *argv [])
{
	Singleton *singletonObj = Singleton ::GetInstance();
	cout<<singletonObj->GetTest()<<endl; return 0;
}
  
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 
文章来源: blog.csdn.net,作者:IM-STONE,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/doubleintfloat/article/details/79824804
- 点赞
 - 收藏
 - 关注作者
 
            
           
评论(0)