C++实现单例模式

设计模式:

设计模式是一套被反复使用,多数人知晓的,经过分类的,代码设计经验的总结。

使用设计模式的目的为了代码可重用性,让代码更容易被他人理解,保证代码可靠性。设计模式使代码编写真正工程化;设计模式是软件工程的基础脉络,如同大厦的结构一样。

单例模式:

一个类只能创建一个对象,既单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件 中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置 信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现模式

1.饿汉模式

饿汉模式就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象

代码实现

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return &m_instance;
	}
private:
	//构造函数私有
	Singleton()
	{};
	//防止拷贝
	Singleton(Singleton const&);
	//C++11 delete删除函数
	Singleton& operator=(Singleton const&s1) = delete;
	static Singleton m_instance;
};
Singleton Singleton::m_instance;//在程序入口前就完成单例对象的初始化
int main()
{
	//Singleton s1;在定义变量m_instance时就已经创建了对象,再次创建对象时会失败
	//Singleton s(*Singleton::GetInstance());用拷贝构造创建对象也会失败,因为拷贝函数被声明为私有的
	//Singleton s1 = *Singleton::GetInstance();运算符重载也会创建失败,因为运算符重载并声明为已删除函数并且还是私有的
	Singleton::GetInstance();
	return 0;
}

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

2.懒汉模式

懒汉模式就是需要实例,实例要被用到的时候才会被加载

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好

代码实现

 

#include <iostream>
using namespace std;

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (m_instance == nullptr)
		{
			m_instance = new Singleton();
		}
		return m_instance;
	}
private:
	Singleton();//构造函数私有化,保证其他地方不能直接创建对象
	//防止拷贝
	Singleton(const Singleton&);
	
	//C++11 delete删除函数
	Singleton &operator = (const Singleton& s1) = delete;

	static Singleton* m_instance;
};

Singleton *Singleton::m_instance = nullptr;

int main()
{
	Singleton::GetInstance();
	return 0;
}

但是如果在类析构行为有必须的操作,例如要关闭文件,释放外部资源,那么上面的代码无法实现这个要求。因为这个指向对象的指针是一个静态成员,无法调用本类的析构函数

这里有两种方法,正常的删除该实例

1.可以在程序结束时调用Getinstance(),并对返回的指针掉用delete操作。但是这样做容易出错。因为很难保证在delete之后,没有代码再调用GetInstance函数。

2.让这个类把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。我们知道程序在结束的时候,系统会自动析构所有的全局变量和所有的类的静态成员变量。利用这个特征,我们可以在单例类中定义一个静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。

#include<iostream>
using namespace std;
class Singleton
{
public:
	static Singleton* Getinstance()
	{
		if (m_instance == nullptr)
		{
			m_instance = new Singleton();
		}
		return m_instance;
	}
	void Print()
	{
		cout << "懒汉模式" << endl;
	}
	//定义一个内部垃圾回收类,它是外部类的友元类,可以访问外部类的成员
	class CGarbo
	{
	public:
		~CGarbo()
		{
			if (Singleton::m_instance)
				delete Singleton::m_instance;
		}
		void Print1()
		{
			cout << "析构函数" << endl;
		}
		//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
		static CGarbo Garbo;
	};
private:
	Singleton()//构造函数私有
	{}
	//防止拷贝
	Singleton1(const Singleton1& sl);
	//C++11 delete删除函数
	Singleton1& operator=(const Singleton1& sl) = delete;

	static Singleton* m_instance;
};
Singleton* Singleton::m_instance = nullptr;
Singleton::CGarbo Garbo;
int main()
{
	Singleton::Getinstance()->Print();
	return 0;
}

上述的类可以很好的解决释放的问题。但是依然存在很多问题,例如线程安全的问题,如果两个线程同时走到new的时候,指针m_instance只有一份,而两个线程都会new一份空间,那么这两块空间应该怎么分配给这个指针呢?这就导致了线程安全的问题。

我们可以用互斥锁和双检查来解决这个问题:

#include<iostream>
using namespace std;
#include <mutex> //互斥量
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		//double check(可以提高效率,如果没有双检查,原本并行的线程到这里都变成串行的)
		if (m_instance == nullptr)
		{
			m_mtx.lock();//相当于二元信号量,只允许一个人进去
			if (m_instance == nullptr)
				m_instance = new Singleton;
			m_mtx.unlock();
		}
		return m_instance;
	}
	void Print()
	{
		cout << "懒汉模式" << endl;
	}
	//实现一个垃圾回收内部类
	class CGarbo
	{
	public:
		~CGarbo()
		{
			cout << "删除" << endl;
		}
		//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
		static CGarbo Garbo;
	};
private:
	Singleton()//构造函数私有
	{}
	//防止拷贝
	Singleton(const Singleton&);
	//C++11 delete删除函数
	Singleton& operator=(const Singleton&) = delete;

	static Singleton* m_instance;//单列对象指针
	static mutex m_mtx; //互斥锁
};
mutex Singleton::m_mtx;
Singleton::CGarbo Garbo;
Singleton* Singleton::m_instance = nullptr;
int main()
{
	Singleton::GetInstance()->Print();
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值