C++单例模式

在编程的时候,有一些场景经常需要我们保证这个类只能存在一个对象,这就叫做单例模式。

什么是单例模式?

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

单例模式的分类

单例模式的实现一般分为饿汉模式和懒汉模式。

饿汉模式

所谓饿汉模式,就是"饿"了,急需要吃,即创建出对象,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

懒汉模式

所谓懒汉模式,是指比较"懒",第一个人需要使用时再去实例对象。

实现单例模式

下面我们来实现一下单例模式,以便更深刻了解

饿汉模式

因为需要一个类只能存在一个对象,所以首先我们需要把构造函数私有吧,避免外面有人直接去实例化对象,其次也需要把拷贝构造封掉,因为可能会有人拿唯一的对象去拷贝构造出一个新的,那就还有赋值重载。

cpp 复制代码
#include<iostream>
#include<vector>

using namespace std;

class hungersingle
{
private:
	hungersingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" })
		:_x(x)
		, _y(y)
		, vs(vv)
	{
		cout << "hunfersingle构造" << endl;
	}
	hungersingle(const hungersingle&) = delete;
	hungersingle operator=(const hungersingle&) = delete;
	int _x;
	int _y;
	vector<string> vs;
};

而我们需要保证在程序运行前只能存在一个对象,那这个对象必须是"全局"的,所以我们可以用一个静态成员变量,该类只有一个静态成员。

cpp 复制代码
static hungersingle hs;

我们提供一个接口去获取该静态成员即可

cpp 复制代码
static hungersingle* getobject()
{
	return &hs;
}

这样一个饿汉单例模式就好啦,饿汉是比较简单的。再给他提供一些接口测试一下

cpp 复制代码
class hungersingle
{
public:
	static hungersingle* getobject()
	{
		return &hs;
	}
	void Print()
	{
		cout << _x << endl;
		cout << _y << endl;
		for (auto& e : vs)
			cout << e << " ";
		cout << endl;
	}
	void addstr(const string& str)
	{
		vs.emplace_back(str);
	}
	void changeINT(int m, int n)
	{
		_x = m;
		_y = n;
	}
	hungersingle(const hungersingle&) = delete;
	hungersingle operator=(const hungersingle&) = delete;

private:
	hungersingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" })
		:_x(x)
		, _y(y)
		, vs(vv)
	{
		cout << "hunfersingle构造" << endl;
	}
	int _x;
	int _y;
	vector<string> vs;
	static hungersingle hs;//类内声明
};
hungersingle hungersingle::hs;//类外面定义

饿汉单例模式虽然简单,但是也是会有一些缺点的,因为它需要在程序启动时main函数之前就把对象实例化出来,我们这个类比较简单,如果有的类实例化对象时需要申请大量资源,那么程序运行后半天实例化不出对象,可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取

文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,

就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式更好。

懒汉模式的核心思想是有一个静态成员指针,第一个用它的人去实例化。

首先它也需要构造函数私有,封掉拷贝构造和复制重载

cpp 复制代码
#include<iostream>
#include<vector>
#include<mutex>
#include<string>
using namespace std;


class lazysingle
{
	lazysingle(const lazysingle&) = delete;
	lazysingle operator=(const lazysingle&) = delete;

private:
	lazysingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" })
		:_x(x)
		, _y(y)
		, vs(vv)
	{

	}
	~lazysingle()
	{
		cout << "~lazysingle()" << endl;
	}
	int _x;
	int _y;
	vector<string> vs;
	static lazysingle* ls_ptr;
};
lazysingle* lazysingle::ls_ptr = nullptr;

刚才说了懒汉的思想是第一个人去实例化对象,即

cpp 复制代码
	static lazysingle* getobject()
	{
		if (ls_ptr == nullptr)
		{
			ls_ptr = new lazysingle;
		}
		
		return ls_ptr;
	}

但是我们需要注意的是,如果是在多线程场景下,这是有线程安全问题的,因为如果两个执行流几乎同时执行if (ls_ptr == nullptr),第一个执行流刚进到if内部,因为时间片到了被切出去,第二个执行流又去判断if (ls_ptr == nullptr)也进去就会出问题。所以这里需要加锁保证安全。

cpp 复制代码
	static lazysingle* getobject()
	{
		if (ls_ptr == nullptr)
		{
			unique_lock<mutex> lock(_mx);//用锁防止同时有人来申请
			if (ls_ptr == nullptr)
				ls_ptr = new lazysingle;
		}
		
		return ls_ptr;
	}

因为懒汉这个这个对象是new出来的,所以不要忘记delete,为了防止内存泄漏,可以托管给其它对象处理,即

cpp 复制代码
static void Destroy()
{
	if (ls_ptr)
	{
		delete ls_ptr;
		ls_ptr = nullptr;
	}
			
}


class Gc//定义这个类让他的析构自动调用lazysingle的destroy,防止忘记调用
{
public:
	~Gc()
	{
		lazysingle::Destroy();
	}
};
static Gc gc;

先写一个destroy函数调用delete,再写一个内部类,创建一个静态成员变量,当程序结束时该变量调用析构自动去调用lazysingle::Destroy();完整代码如下:

cpp 复制代码
class lazysingle
{
public:
	static lazysingle* getobject()
	{
		if (ls_ptr == nullptr)
		{
			unique_lock<mutex> lock(_mx);//用锁防止同时有人来申请
			if (ls_ptr == nullptr)
				ls_ptr = new lazysingle;
		}
		
		return ls_ptr;
	}
	void Print()
	{
		cout << _x << endl;
		cout << _y << endl;
		for (auto& e : vs)
			cout << e << " ";
		cout << endl;
	}
	static void Destroy()
	{
		if (ls_ptr)
		{
			delete ls_ptr;
			ls_ptr = nullptr;
		}
				
	}
	void addstr(const string& str)
	{
		vs.emplace_back(str);
	}
	void changeINT(int m, int n)
	{
		_x = m;
		_y = n;
	}
	
	lazysingle(const lazysingle&) = delete;
	lazysingle operator=(const lazysingle&) = delete;

private:
	lazysingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" })
		:_x(x)
		, _y(y)
		, vs(vv)
	{

	}
	~lazysingle()
	{
		cout << "~lazysingle()" << endl;
	}
	int _x;
	int _y;
	vector<string> vs;
	static lazysingle* ls_ptr;
	static mutex _mx;

	class Gc//定义这个类让他的析构自动调用lazysingle的destroy,防止忘记调用
	{
	public:
		~Gc()
		{
			lazysingle::Destroy();
		}
	};
	static Gc gc;
};
mutex lazysingle::_mx;
lazysingle* lazysingle::ls_ptr=nullptr;
lazysingle::Gc lazysingle::gc;

main函数执行下面语句

cpp 复制代码
int main()
{
	lazysingle::getobject()->Print();

	return 0;
}

C++ 11 对static局部变量的初始化语义进行了改进,使得在多线程环境下,static局部变量的初始化是线程安全的,即原子的。

所以C++11之后可以对懒汉模式不加锁实例化静态局部对象

cpp 复制代码
class lazysingle
{
public:
	static lazysingle* getobject()
	{
		static lazysingle laz;//定义局部静态对象,只有第一次调用这个函数的时候才会创建这个对象//C++11后这是一步原子的操作
		return &laz;
	}
	void Print()
	{
		cout << _x <<endl;
		cout << _y << endl;
		for (auto& e : _vs)
			cout << e << " ";
		cout << endl;
	}
	void addstr(const string& ret)
	{
		_vs.push_back(ret);
	}
	void addINT(int m,int n)
	{
		_x = m;
		_y = n;
	}
	lazysingle(const lazysingle&) = delete;
	lazysingle operator=(const lazysingle&) = delete;

private:
	lazysingle(int x=100,int y=200,vector<string> vs={"高考","中考","殿试"})
		:_x(x)
		,_y(y)
		,_vs(vs)
	{
		cout << "lazysingle构造" << endl;
	}

	int _x;
	int _y;
	vector<string> _vs;
};
相关推荐
无己心2 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u0103055278 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055279 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝10 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552710 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552711 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552713 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552713 天前
Java I/O核心操作实战
人工智能·1024程序员节
u01030552714 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节
u01030552715 天前
Java模块化编程实战指南
人工智能·笔记·1024程序员节