单例模式的实现

  • Post author:
  • Post category:其他


(一)单例模式的设计目的

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点;

(1)基于全局访问点的思想,很容易想到定义全局或者静态对象,但是这样不能保证该类只声明一个静态对象,并没有实现仅有一个对象的思想;

(2)单例模式设计方式

  • 将类的构造函数写在私有下;
  • 定义一个静态的指针指向该类唯一的对象;
  • 定义一个公有下返回该对象的静态方法

代码如下:

//单例模式
 class CSingleton
 {
 private:
	 CSingleton(){}//构造写在私有下
	 static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
 public:
	 static  CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
	 {
		 if(m_pInstance == NULL)
		 {
			 m_pInstance = new CSingleton();
	         }
 
		return m_pInstance;//指针指向唯一的实例化对象
	 }
 };

上述代码实现了单例模式,但是其在多线程下不安全,下图中模拟了一种线程不安全的情景:

可以从图中看到if 中的代码相当于临界区,所以访问临界区资源时需要进行同步控制,故而我们引入了锁机制,代码如下:

//单例模式
 class CSingleton
 {
 private:
	 CSingleton(){}//构造写在私有下
	 static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
 public:
	 static  CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
	 {
			
		 lock();
		 if(m_pInstance == NULL)
		 {
			 m_pInstance = new CSingleton();
		 }
		 unlock();
		
		 return m_pInstance;//指针指向唯一的实例化对象
	 }
 };

分析上述代码:

(1)首先A线程执行代码,拿到资源先加锁,判断m_pInstance是否为空,如果不为空则构造对象;直到实例化对象后解锁

(2)B线程同时在执行代码时发现已被其他线程加锁,则不进入,只有解锁后,B判断m_pInstance已经不为空,则不进行构造;

这样解决了线程安全问题,接下来引入一种双重安全控制代码,这样保证了如果此对象已经被构造,则此线程就不再访问到临界区,这样更安全,代码如下:

//单例模式
 class CSingleton
 {
 private:
	 CSingleton(){}//构造写在私有下
	 static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
 public:
	 static  CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
	 {
		 if((m_pInstance == NULL)//在外层控制线程是否要接触到临界资源
		 {
			 //如果为空则加锁构造对象;
			 //如果不为空则接触到临界资源
			 lock();
			 if(m_pInstance == NULL)
			 {
				 m_pInstance = new CSingleton();
			 }
			 unlock();
		 }
		 return m_pInstance;//指针指向唯一的实例化对象
	 }
 };

(3)什么时候释放合适呢?我们可以在其私有下设计一个内嵌类,专门用来释放程序运行结束后对象所占用的资源,必须写在私有下的原因是防止其文件的调用类内析构函数释放对象。代码如下:

class CSingleton
 {
 private:
	 CSingleton(){}//构造写在私有下
	 static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
	//写在私有下  专门用来析构对象所占资源
	 class  A_gorb
	 {
	 public:
		 ~A_gorb()
		 {
			 delete CSingleton::m_pInstance;
		 }
	 };
	 static A_gorb a_g;
 public:
	 static  CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
	 {
		 if((m_pInstance == NULL)//在外层控制线程是否要接触到临界资源
		 {
			 //如果为空则加锁构造对象;
			 //如果不为空则接触到临界资源
			 lock();
			 if(m_pInstance == NULL)
			 {
				 m_pInstance = new CSingleton();
			 }
			 unlock();
		 }
		 return m_pInstance;//指针指向唯一的实例化对象
	 }
 };

(4)单例模式的使拥实例

  • 打印机:只要有打印任务在进行,就必须等待当前打印任务结束在打印,一个PC端只能在同一时刻连接一台打印机;
  • 系统的日志文件:只需要一个日志文件记录系统的状态信息
  • 网站的计数器,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题

(5)应用场景特点总结


  • 需要生成唯一序列的环境

  • 需要频繁实例化然后销毁的对象。

  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

  • 方便资源相互通信的环境



版权声明:本文为weixin_41966991原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。