第十课 设计模式之单例模式

文章详细介绍了如何在C++中实现单例模式,从构造函数私有化、静态成员函数、防止多线程环境下创建多个实例,到懒汉式和饿汉式的不同实现策略,并讨论了各自的优缺点。最后提到了多线程环境下的单例模式需考虑同步问题。
摘要由CSDN通过智能技术生成

一、单例模式

首先我们应该明确,一个单例模式应该满足以下3点:

1. 当前类最多只能创建一个实例。
2. 自己创建这个实例(而不是调用者创建)。
3. 向整个系统提供全局访问点访问这个实例。

首先,我们先写一个普通的类:

#include<iostream>
using namespace std;

class CSingleton {


};

int mian() {

	return 0;
}

(一)我们考虑如何做到让一个类,只能自己创建实例

构造私有化

#include<iostream>
using namespace std;

class CSingleton {
private://只能类内调用
	CSingleton() {

	}
};

int mian() {

	return 0;
}

之后我们需要一个类外调用的接口

#include<iostream>
using namespace std;

class CSingleton {
private:
	CSingleton() {

	}

	CSingleton* CreateSingleton()
	{
		CSingleton* pin = new CSingleton;
		return pin;
	}

};

int mian() {

	return 0;
}

 但是我们不能确保这个对象是唯一的,所以我们可以让pin作为一个旗帜,如果不为空才可以被创建。

#include<iostream>
using namespace std;

class CSingleton {
private:
	CSingleton* Pin;

	CSingleton():Pin(nullptr){

	}
	

	CSingleton* CreateSingleton()
	{
		if(!Pin)
			Pin = new CSingleton;
		return Pin;
	}

};

int mian() {

	return 0;
}

但是此时我们想要调用此单例时我们会发现,首先我们将其改为公共类型,其次我们必须创建对象才可以使用,为了解决这个问题,我们可以想到,类中不依赖对象存在的函数——静态函数

所以我们将创造函数修改为静态函数

注意:静态函数只能调用静态成员属性,所以pin也需要进行修改,及其初始化位置

#include<iostream>
using namespace std;

class CSingleton {
private:
	static CSingleton* Pin;

	CSingleton(){

	}
	
public:
	static CSingleton* CreateSingleton()
	{
		if(!Pin)
			Pin = new CSingleton;
		return Pin;
	}

};
CSingleton* CSingleton:: Pin = nullptr;

int mian() {
	

	return 0;
}

 之后我们来测试一下

#include<iostream>
using namespace std;

class CSingleton {
private:
	static CSingleton* Pin;

	CSingleton(){

	}
	
public:
	static CSingleton* CreateSingleton()
	{
		if(!Pin)
			Pin = new CSingleton;
		return Pin;
	}

};
CSingleton* CSingleton:: Pin = nullptr;

int main() {

	CSingleton* pSin1 = CSingleton::CreateSingleton();
	CSingleton* pSin2 = CSingleton::CreateSingleton();

	cout << pSin1 << "    " << pSin2 << endl;
	return 0;
}

结果为

 但是我们会发现,我们申请的空间并没有被回收,所以我们需要一个回收函数

#include<iostream>
using namespace std;


class CSingleton
{
private:
	static CSingleton* pSin;
	CSingleton(){

	}
public:
	static CSingleton* CreateSingleton() {
		if(!pSin)
			pSin = new CSingleton;
		return pSin;
	}
	static void DestorySingleton()
	{
		if (pSin)
		{
			delete pSin;
			pSin = nullptr;
		}
	}
};
CSingleton* CSingleton::pSin = nullptr;
int main() {
	CSingleton* pSin1 = CSingleton::CreateSingleton();
	CSingleton* pSin2 = CSingleton::CreateSingleton();
	cout << pSin1 << "   " << pSin2 << endl;
	return 0;
}

同时,我们考虑是否能够将主函数中指向单例的指针一同销毁,我们可以写一个带参数的销毁函数,且由于我们需要修改内容,所以传参方式我们选择引用传参

#include<iostream>
using namespace std;


class CSingleton
{
private:
	static CSingleton* pSin;
	CSingleton(){

	}
public:
	static CSingleton* CreateSingleton() {
		if(!pSin)
			pSin = new CSingleton;
		return pSin;
	}
	static void DestorySingleton(CSingleton*& psin)
	{
		if (pSin)
		{
			delete pSin;
			pSin = nullptr;
		}
		psin = nullptr;
	}
};
CSingleton* CSingleton::pSin = nullptr;
int main() {
	CSingleton* pSin1 = CSingleton::CreateSingleton();
	CSingleton* pSin2 = CSingleton::CreateSingleton();
	cout << pSin1 << "   " << pSin2 << endl;
	CSingleton::DestorySingleton(pSin1);
	CSingleton::DestorySingleton(pSin2);
	return 0;
}

但是此写法依旧存在问题,例如当下列情况出现时

CSingleton* pSin1 = CSingleton::CreateSingleton();
	CSingleton pSin(*pSin1);
	cout << pSin1 << "   " <<&pSin<< endl;

结果仍出现两个不同的实例

为了解决这种情况,我们可以将拷贝构造函数进行弃用

class CSingleton
{
private:
	static CSingleton* pSin;
	CSingleton(){

	}
	CSingleton(const CSingleton&) = delete;//禁用拷贝构造
	~CSingleton()
	{

	}
public:
	static CSingleton* CreateSingleton() {
		if(!pSin)
			pSin = new CSingleton;
		return pSin;
	}
	static void DestorySingleton(CSingleton*& psin)
	{
		if (pSin)
		{
			delete pSin;
			pSin = nullptr;
		}
		psin = nullptr;
	}

};

到此,一个单例模式差不多完成了,但是其实还存在一个隐形的问题:此单例模式在单线程下可以实现,但多线程需要加锁,之后会进行补充 

1.1懒汉式

当调用单例的接口时才会创建

上述方法就是一种懒汉式

下面展示懒汉式的另一种实现

刚刚我们提到上面单例多线程下会创建多个,我们可以这样写

#include<iostream>
using namespace std;


class CSingleton
{
private:
	static CSingleton* pSin;
	CSingleton() {

	}
	CSingleton(const CSingleton&) = delete;
	~CSingleton()
	{

	}
public:
	static CSingleton* CreateSingleton()
	{
		static CSingleton sin;
		return &sin;
	}
};
int main()
{
	CSingleton *pSin1 = CSingleton::CreateSingleton();
	cout << pSin1 << endl; 
	CSingleton* pSin2 = CSingleton::CreateSingleton();
	cout << pSin2 << endl;
	return 0;
}

运行结果为

 

这种实现方法与第一种相比,虽然避免了多线程下创建多个的问题,但是却无法将单例手动销毁 

时间换空间的,直到第一次调用,才会执行构造,开辟空间。

1.2饿汉式

饿汉式和懒汉式相反,

饿汉式是一开始就将对象创建出来,相当于空间换时间的做法。

那我们就会考虑那种方式可以产生这种效果——静态类成员属性

#include<iostream>
using namespace std;


class CSingleton
{
private:
	static CSingleton sin;
	static CSingleton* pSin;
	CSingleton() {

	}
	CSingleton(const CSingleton&) = delete;
	~CSingleton()
	{

	}
public:
	static CSingleton* CreateSingleton()
	{
		
		return &sin;
	}
};
CSingleton CSingleton::sin;
int main()
{
	CSingleton *pSin1 = CSingleton::CreateSingleton();
	cout << pSin1 << endl; 
	CSingleton* pSin2 = CSingleton::CreateSingleton();
	cout << pSin2 << endl;
	return 0;
}

在懒汉式的基础上,稍加改进...... 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值