C++单例模式总结

今天好好总结一下单例模式

  • 首先什么是单例模式:
    《设计模式》一书中给出的定义是:让类自身负责保存它的唯一实例,这个类可以保证没有其他实例被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法,这就是Singleton模式。有人叫单例模式,也有叫单件模式。那么如何实现以上要求呢?首先,需要保证一个类只有一个实例;在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。

####单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化;
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
###特点与选择:
由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
在访问量较小时,采用懒汉实现。这是以时间换空间。
###C++代码实现

  • 经典版:
#include 
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;
}

这是最简单,也是最普遍的实现方式,也是现在网上各个博客中记述的实现方式,但是,这种实现方式,有很多问题,比如:没有考虑到多线程的问题,在多线程的情况下,就可能创建多个Singleton实例,以下版本是改善的版本。

  • 经典版优化:-
#include 
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;
}
  • 内部静态变量的懒汉实现:-
    此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
#include 
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;
}

因为static Singleton *GetInstance()是不可重入函数,所以存在线程安全问题,访问静态变量时要加锁;那么一个函数要成为可重入函数,必须要具备以下几个特点:
1、仅依赖于调用方传入的参数
2、不依赖于任何单个资源的锁
3、不反回任何(局部)静态或全局的非const变量的指针
4、不使用任何(局部)静态或全局的非const变量
5、不调用任何不可重入函数
可重入是并发安全的强力保证,一个可重入函数可以在多线程环境下放心使用。

  • 饿汉实现:-
#include 
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;
}

因为静态初始化在程序开始时,也就是进入主函数之前,由主线程以单线程方式完成了初始化,所以静态初始化实例保证了线程安全性。在性能要求比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁造成的资源浪费。

  • 实例销毁:-
#include 
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;
}

参考:http://blog.jobbole.com/108579/
https://www.cnblogs.com/qiaoconglovelife/p/5851163.html

你可能感兴趣的:(C++深入进阶,设计模式C++)