设计模式之单例模式(C++实现)

1、在C++中,单例模式可以直接用全局变量替代,但这样。能够保证方便地访问实例,但是不能保证只声明一个对象——也就是说除了一个全局实例外,仍然能创建该类的局部实例。

Ps:全局变量的使用:
		①、存在类StudentManager。
		②、在头文件里面声明对象:Extern StudentManager g_StudentManager;
		③、在源文件里面添加:StudentManager g_StudentManager;
		④、在其他文件里面包含头文件:#include "StudentManager.h"	即可通过g_StudentManager访问StudentManager类的数据。		

2、但是仍然可以在工程其他文件通过 StudentManager studentManager;这样的方式创建对象,往往在一些特定的场景仅仅要求创建一个实例,那么全局变量也就不管用了,此时需要使用单例模式。应用场景比如:Windows系统只能有一个光标了;唯一的资源任务管理器;打印机只能打印一份报表等等。
3、单例模式是使用广泛的一种设计模式,通俗来讲就是仅允许内存中存在一个指定类的对象。
具体来看有几点要求:
①保证一个类仅有一个实列
②并提供一个访问它的全局访问点
③该实例被工程所有模块共享。
4、设计思想:定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。
5、此模式分饿汉、懒汉两种情况。
5.1 饿汉模式:
饿了就吃,丝毫不耽误。在单例类定义的时候就进行实例化,不允许抢占资源,线程安全。

/*
SingleTon类
*/
#pragma once
#include <iostream>
#include <string>
using namespace std;

class SingleTon
{
private:
	SingleTon(){}			  //私有化的构造函数,从此不可在其他地方实例化该类。
private:
	static SingleTon* m_pSingle;//唯一一个指向该类示例的静态指针,且作用域私有。
	static string m_strTip;
	
public:	
	static SingleTon* GetInstance()	//定义一个公有函数,可以获取这个唯一的实例,并且仅在需要的时候出来创建该实例。
	{
		return m_pSingle;
	}

	void Show()
	{
		cout << m_strTip << endl;
	}
};
/*
main()函数调用。
*/
#include "main.h"

SingleTon* SingleTon::m_pSingle = new SingleTon;	//饿汉模式初始化即创建对象
string SingleTon::m_strTip = "获取实例成功";//static 定义的数据成员必须放到类外初始化,因为它是整个类的一部分,不属于某个对象。

int main()
{
	for (int i = 0; i < 10;i++)
	{
		SingleTon* pSingleTest1 = SingleTon::GetInstance();
		cout << pSingleTest1 << endl;
		pSingleTest1->Show();
	}

	cout << "over,over" << endl;
	cin.get();
	return 0;
}

5.2 类特征。
①、它有一个指向唯一实例的静态指针p,并且是私有的。
②、它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例。
③、它的构造函数是私有的,这样就不能从别处创建该类的实例。
错误示例:
在这里插入图片描述

运行:地址唯一,成功。
在这里插入图片描述
5.3 看完这段代码,我们需要思考一个问题。
我们知道,静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放。因此以上程序在结束的时候,系统会自动析构new出来的变量。因此我们现在不必担心申请的内存归还问题。
但是,在某些特定场景中,我们需要通过单例对象释放掉一些被该类占用的其他系统资源。这就需要 处理更加详细的析构了。当然我们可以在需要做析构的时候,去通过GetInstance()函数来获取到该实例,进而去处理更加详细的析构。那么有没有更加稳健的方法,让单例类自己去处理析构呢?当然存在。我们需要在SingleTon类里面添加一个自动析构类,去处理SingleTon的析构。

/*
SingleTon类
*/
#pragma once
#include <iostream>
#include <string>
using namespace std;

class SingleTon
{
private:
	SingleTon(){}			  //私有化的构造函数,从此不可在其他地方实例化该类。

	class AutoDestroy		 //它的唯一工作就是在析构函数中删除CSingleton的实例
	{
	public:
		~AutoDestroy()		 //析构SingleTon指针
		{
			if (SingleTon::m_pSingle)
			{
				delete SingleTon::m_pSingle;
			}
		}
	};
private:
	static SingleTon* m_pSingle;//唯一一个指向该类示例的静态指针,且作用域私有。
	static string m_strTip;
	
	static AutoDestroy autoDeser;
public:	
	static SingleTon* GetInstance()//定义一个公有函数,可以获取这个唯一的实例,并且仅在需要的时候出来创建该实例。
	{
		return m_pSingle;
	}

	void Show()
	{
		cout << m_strTip << endl;
	}
};
/*
main()函数调用。
*/
#include "main.h"

string SingleTon::m_strTip = "获取实例成功";//static 定义的数据成员必须放到类外初始化,因为它是整个类的一部分,不属于某个对象。
SingleTon::AutoDestroy SingleTon::autoDeser;
SingleTon* SingleTon::m_pSingle = new SingleTon;	//饿汉模式初始化即创建对象

int main()
{
	for (int i = 0; i < 10;i++)
	{
		SingleTon* pSingleTest1 = SingleTon::GetInstance();
		cout << pSingleTest1 << endl;
		pSingleTest1->Show();
	}

	cout << "over,over" << endl;
	cin.get();
	return 0;
}

5.4 懒汉模式:
相当懒,不到火烧眉毛不动弹。需要使用该类对象才去实例化,线程不安全。
比如:现在有A线程和B线程,A线程刚好在这个GetInstance()方法中,刚刚判断完非空(此时为NULL),即需要创建实例。与此同时,B线程抢到了CPU的执行权,A线程sleep了。这时,B线程也进行了这个判断,和A一样,都需要创建实例。而这时,A也抢到了CPU,这时B就sleep了,然后A执行了实例化后,B又抢到CPU执行权,然后B也实例化,这时,出现问题了,A和B都实例化了一个对象,破坏了单例的唯一性。

/*
SingleTon类
*/
#pragma once
#include <iostream>
#include <string>
using namespace std;

class SingleTon
{
private:
	SingleTon(){}			  //私有化的构造函数,从此不可在其他地方实例化该类。

	class AutoDestroy		 //它的唯一工作就是在析构函数中删除CSingleton的实例
	{
	public:
		~AutoDestroy()		 //析构SingleTon指针
		{
			if (SingleTon::m_pSingle)
			{
				delete SingleTon::m_pSingle;
			}
		}
	};
private:
	static SingleTon* m_pSingle;//唯一一个指向该类示例的静态指针,且作用域私有。
	static string m_strTip;
	
	static AutoDestroy autoDeser;
public:	
	static SingleTon* GetInstance()//定义一个公有函数,可以获取这个唯一的实例,并且仅在需要的时候出来创建该实例。
	{
		if (!m_pSingle)				//判断是否是第一次调用。<懒汉>
		{
			m_pSingle = new SingleTon;
		}
		return m_pSingle;
	}

	void Show()
	{
		cout << m_strTip << endl;
	}
};
/*
main()函数调用。
*/
#include "main.h"
SingleTon* SingleTon::m_pSingle = NULL;			//饱汉模式需要用到该类对象时才去实例化
string SingleTon::m_strTip = "获取实例成功";//static 定义的数据成员必须放到类外初始化,因为它是整个类的一部分,不属于某个对象。
SingleTon::AutoDestroy SingleTon::autoDeser;

int main()
{
	for (int i = 0; i < 10;i++)
	{
		SingleTon* pSingleTest1 = SingleTon::GetInstance();
		cout << pSingleTest1 << endl;
		pSingleTest1->Show();
	}

	cout << "over,over" << endl;
	cin.get();
	return 0;
}

5.5 二者取舍。
懒汉:不要求线程同步,访问量较小时,采用懒汉模式。以时间换空间。
饿汉:要求线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。实操中,我们可以加大内存来以空间换时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值