C++:特殊类和单例模式

文章目录

不能被拷贝的类

设计一个不能被拷贝的类,通常来说方法就是把拷贝构造和赋值重载都设置为私有,这样就不能进行拷贝了

cpp 复制代码
class A
{
public:
	A()
	{}
private:
	A(const A& a);
	A& operator=(const A& a);
};

对于C++11来说,新增了delete的用法,可以直接删除这个函数,也能做到相同的效果

cpp 复制代码
class A
{
public:
	A()
	{}
	A(const A& a) = delete;
	A& operator=(const A& a) = delete;
private:
};

设计一个只能在堆上创建对象的类

这是一个比较奇怪的要求,但是也有对应实现的方法:

  1. 把类的构造函数设置为私有
  2. 提供一个静态的成员函数,通过函数调用来进行堆上对象的创建
cpp 复制代码
// 将构造函数都设置为私有
class A
{
private:
	A();
	A(const A&);
	A& operator=(const A&);
};

int main()
{
	A* pa = new A;
	A a;
}

这样就可以保证无法创建出对象,但是同样的也无法直接new出一个对象,因为new操作符底层是调用了operator new和构造函数

解决的方法是,设置一个静态的成员函数,用来完成堆上对象的创建

cpp 复制代码
class A
{
public:
	static A* create()
	{
		A* pa = new A;
		return pa;
	}
private:
	A();
	A(const A&);
	A& operator=(const A&);
};

int main()
{
	A* pa = A::create();
	return 0;
}

到这里正好进行回顾一下static修饰成员函数表示的意义:

  1. static修饰成员函数,代表的是这个成员函数在类作用域内是全局函数,但是不能调用非静态成员变量和成员函数
  2. static修饰成员函数,该成员函数没有this指针

所以基于这样的原因,就可以设计出上面的特殊类,把构造函数都私有化,但是保留一个静态成员函数,这样就可以通过类的作用域来调用new一个对象出来,但不能在栈上开辟空间

设计一个类,只能在栈上创建对象

这个实现的逻辑其实和前面基本相同,但是唯一的区别是要防止通过new操作符,因此可以这样设计

cpp 复制代码
class A
{
public:
	static A create()
	{
		return A();
	}
	
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	A()
	{}
};

int main()
{
	A a = A::create();
	A* pa = new A(a);
	return 0;
}

单例模式:设计一个只能创建一个对象的类

什么是单例模式?

单例模式指的是,一个类只能创建一个对象,保证系统中只存在这样一个实例,提供访问它的全局访问点,这个实例被所有的程序模块所共享

使用场景?

简单来说,单例模式的使用场景之一就是对于一些特殊的情况,例如对于内存池来说,如果使用的不是单例模式,可能会创建出很多的内存池,那么在进行空间的申请就会有很奇怪的错误出现,不方便管理内存,因此有了单例模式,不管在什么场景下都只能有一个实例,所有的操作都必须在这个实例下进行,就完成了单例模式设计的初衷

如何设计单例模式?

单例模式的设计通常有两种,一种是饿汉模式,一种是懒汉模式,那么下面就基于这两种模式分别进行设计

饿汉模式

不管你将来用不用,程序启动时就创建一个唯一的实例对象'''

如何保证是只有一个?保证的原理基础是用static修饰成员变量,但这个成员变量就是一种类的实例

static修饰成员变量?

static修饰成员变量,表示一种声明,初始化要在类体外进行初始化,简单来说就是只是在类的内部声明了有这样的一个实例,但是并不占用类的内部内容,并且更便携的是,它还属于这个类,就意味着它可以调用类的内部成员函数完成一些内容,并且整个作用域内只有一份

cpp 复制代码
// 单例
// 饿汉模式:提前(main函数启动时)创建好实例对象
// 优点:实现简单
// 缺点:1、可能会导致进程启动慢、2、如果两个单例有启动先后顺序,那么饿汉无法控制
class A
{
public:
	static A* GetInstance()
	{
		return &_inst;
	}

	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
private:
	A()
	{}

	A(const A& aa) = delete;
	A& operator=(const A& aa) = delete;

	map<string, string> _dict;
	int _n = 0;

	static A _inst;
};

A A::_inst;

从上面的示例代码中其实可以看出这样的原理,将这个成员变量用static在类内修饰,并且把构造函数和拷贝构造等等的函数都放到私有的部分,这样就可以使得在整个类域中只有一个实例,在外部通过构造函数是无法创建出第二个实例,并且还能调用类内的成员函数,因为它本身其实是属于类的一部分

懒汉模式

懒汉模式在饿汉模式的基础上新增了一个指针的实例化过程,就可以保证在需要的时候进行初始化

cpp 复制代码
// 懒汉模式:第一次用的时候再创建(现吃现做)
// todo:线程安全问题
// new的懒汉对象一般不需要释放,进程正常结束会释放资源
// 如果需要做一些动作,比如持久化,那么可以利用gc类static对象搞定
class B
{
public:
	static B* GetInstance()
	{
		if (_inst == nullptr)
		{
			_inst = new B;
		}

		return _inst;
	}

	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

	static void DelInstance()
	{
		if (_inst)
		{
			delete _inst;
			_inst = nullptr;
		}
	}

private:
	B()
	{}

	~B()
	{
		// 持久化:要求把数据写到文件
		cout << "数据写到文件" << endl;
	}

	B(const B& aa) = delete;
	B& operator=(const B& aa) = delete;

	map<string, string> _dict;
	int _n = 0;

	static B* _inst;

	class gc
	{
	public:
		~gc()
		{
			DelInstance();
		}
	};

	static gc _gc;
};

B* B::_inst = nullptr;
B::gc B::_gc;
相关推荐
charlie1145141914 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
还是大剑师兰特10 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
小林熬夜学编程15 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
倔强的石头10626 分钟前
【C++指南】类和对象(九):内部类
开发语言·c++
Watermelo61730 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导2 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香2 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
Ronin3053 小时前
11.vector的介绍及模拟实现
开发语言·c++