当前位置: 首页 > news >正文

C++特殊类设计

C++特殊类设计

  • 1、请设计一个类,不能被拷贝
  • 2、请设计一个类,只能在堆上创建对象
  • 3、请设计一个类,只能在栈上创建对象
  • 4、请设计一个类,不能被继承
  • 5、请设计一个类,只能创建一个对象(单例模式)
    • 5.1、饿汉模式
    • 5.2、懒汉模式

1、请设计一个类,不能被拷贝

拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C++98方式:将拷贝构造和赋值私有

class CopyBan
{
public:
	CopyBan() {}
private:
	CopyBan(const CopyBan& cb);
	CopyBan& operator=(const CopyBan& cb);
};

1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。


C++11方式:扩展delete用法

class CopyBan
{
public:
	CopyBan() {}
	CopyBan(const CopyBan& cb) = delete;
	CopyBan& operator=(const CopyBan& cb) = delete;
};

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。


2、请设计一个类,只能在堆上创建对象

方式一:析构函数私有

class HeapOnly
{
public:
	void Destroy() {
		delete this;
	}
private:
	~HeapOnly()
	{}
};
HeapOnly* hp = new HeapOnly;
hp->Destroy();

在栈上创建的对象除了作用域会自动调用析构函数,析构函数私有就无法调用了,所以无法在栈上创建对象。
可以在堆上创建对象,但是有个问题,delete的时候无法调用析构函数,因为delete的原理是析构函数+释放空间,所以我们提供一个Destroy接口,直接在函数内部delete,这样就可以调用析构函数。


方式二:构造函数私有

class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
	{}
	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
};
HeapOnly* hp = HeapOnly::CreateObj();

构造函数私有后,既不能在栈上创建,也不能在堆上创建。因此我们需要提供一个CreateObj函数,在里面创建然后将指针返回给外部接收,并且这个函数必须是静态的,否则调不到。

但是这样还是防不住下面这种方式:

HeapOnly* hp = HeapOnly::CreateObj();
HeapOnly copy(*hp);

所以还需要防拷贝。


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

方式:构造函数私有

class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}
private:
	StackOnly()
	{}
	void* operator new(size_t size) = delete;
};
StackOnly st1 = StackOnly::CreateObj();

构造函数私有,然后提供一个静态成员函数,在该成员函数里面创建对象返回。
但是还是防不住下面这种情况:

StackOnly* hp = new StackOnly(st1);

new的原理就是operator new加构造函数,所以我们需要禁掉operator new。


4、请设计一个类,不能被继承

C++98方式:构造函数私有

class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};

C++11方式:final修饰

class A final
{
// ...
};

5、请设计一个类,只能创建一个对象(单例模式)

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

单例模式有两种实现模式:饿汉模式和懒汉模式。

5.1、饿汉模式

之前的CreateObj,不管是new还是栈上创建再返回,都是不同的对象,现在需要的是同一个对象,思考一下该怎么做?

namespace hungry
{
	class Singleton
	{
	public:
		static Singleton& GetInstance()
		{
			return _sinst;
		}

		void Add(const pair<string, string>& kv)
		{
			_dict[kv.first] = kv.second;
		}

		void Print()
		{
			for (const auto& e : _dict)
			{
				cout << e.first << ":" << e.second << endl;
			}
			cout << endl;
		}
	private:
		Singleton()
		{}
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;

		map<string, string> _dict;
		// ...
		static Singleton _sinst;
	};

	Singleton Singleton::_sinst;
}


int main()
{
	cout << &hungry::Singleton::GetInstance() << endl;
	cout << &hungry::Singleton::GetInstance() << endl;
	cout << &hungry::Singleton::GetInstance() << endl;
	hungry::Singleton::GetInstance().Add({ "xxx", "111" });
	hungry::Singleton::GetInstance().Add({ "yyy", "222" });
	hungry::Singleton::GetInstance().Add({ "zzz", "333" });
	hungry::Singleton::GetInstance().Print();
	return 0;
}

在这里插入图片描述
实现:
1、首先将构造函数私有。
2、声明静态类对象,然后在类外定义,提供一个获取对象的静态成员函数GetInstance()。
3、防拷贝,将拷贝和赋值声明为删除。

饿汉模式:就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
但是也有下面的问题:
1、如果单例对象初始化内容很多,影响启动速度。
2、如果有两个单例类,相互依赖关系。假设有A、B两个单例类,要求先创建A,然后再创建B,B的初始化依赖A。


5.2、懒汉模式

namespace lazy
{
	class Singleton
	{
	public:
		static Singleton& GetInstance()
		{
			if (_psinst == nullptr)
			{
				_psinst = new Singleton;
			}
			return *_psinst;
		}
		
		static void DelInstance()
		{
			if (_psinst)
			{
				delete _psinst;
				_psinst = nullptr;
			}
		}
		
		void Add(const pair<string, string>& kv)
		{
			_dict[kv.first] = kv.second;
		}

		void Print()
		{
			for (const auto& e : _dict)
			{
				cout << e.first << ":" << e.second << endl;
			}
			cout << endl;
		}
	private:
		Singleton()
		{}
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;

		~Singleton()
		{
			cout << "~Singleton()" << endl;
			// map数据写到文件中
			FILE* fin = fopen("map.txt", "w");
			for (auto& e : _dict)
			{
				fputs(e.first.c_str(), fin);
				fputs(":", fin);
				fputs(e.second.c_str(), fin);
				fputs("\n", fin);
			}
		}

		map<string, string> _dict;
		// ...
		static Singleton* _psinst;
	};
	Singleton* Singleton::_psinst = nullptr;
}

懒汉模式:第一次调用GetInstance的时候创建单例对象
那么如何释放呢?单例一般不用释放,进程结束直接带走了。
特殊场景:1、中途需要显示释放 2、程序结束时,需要做一些特殊动作(如持久化)

上面的析构函数就是场景2,假设需要把数据写入文件,那么如果是进程结束是不会走析构函数的,无法做到数据持久化。
所以可以提供一个DelIstance函数,中途可以显示释放:

cout << &lazy::Singleton::GetInstance() << endl;
cout << &lazy::Singleton::GetInstance() << endl;
cout << &lazy::Singleton::GetInstance() << endl;
lazy::Singleton::GetInstance().Add({ "xxx", "111" });
lazy::Singleton::GetInstance().Add({ "yyy", "222" });
lazy::Singleton::GetInstance().Add({ "zzz", "333" });
lazy::Singleton::GetInstance().Print();
lazy::Singleton::GetInstance().DelInstance();

在这里插入图片描述


那么如果我不想显示释放,并且还希望数据持久化呢?
可以写这么一个类:

class GC
{
public:
	~GC()
	{
		lazy::Singleton::GetInstance().DelInstance();
	}
};

GC gc;

还可以直接写在Singleton内部:

namespace lazy
{
	class Singleton
	{
	public:
		static Singleton& GetInstance()
		{
			if (_psinst == nullptr)
			{
				_psinst = new Singleton;
			}
			return *_psinst;
		}


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


		void Add(const pair<string, string>& kv)
		{
			_dict[kv.first] = kv.second;
		}

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

		class GC
		{
		public:
			~GC()
			{
				lazy::Singleton::GetInstance().DelInstance();
			}
		};
	private:
		Singleton()
		{}
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;

		~Singleton()
		{
			cout << "~Singleton()" << endl;

			// map数据写到文件中
			FILE* fin = fopen("map.txt", "w");
			for (auto& e : _dict)
			{
				fputs(e.first.c_str(), fin);
				fputs(":", fin);
				fputs(e.second.c_str(), fin);
				fputs("\n", fin);
			}
		}

		map<string, string> _dict;
		// ...
		static Singleton* _psinst;
		static GC _gc;
	};
	Singleton* Singleton::_psinst = nullptr;
	Singleton::GC Singleton::_gc;
}

int main()
{
	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;
	lazy::Singleton::GetInstance().Add({ "xxx", "111" });
	lazy::Singleton::GetInstance().Add({ "yyy", "222" });
	lazy::Singleton::GetInstance().Add({ "zzz", "333" });
	lazy::Singleton::GetInstance().Print();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

相关文章:

  • 18、深拷贝与浅拷贝的区别【中高频】
  • 基于springboot+vue的线上考试系统的设计与实现
  • 使用Java构建高效的Web服务架构
  • 爬虫系列之发送请求与响应《一》
  • 【音视频】VLC播放器
  • 在 Windows 上为流体/结构工具设置 Ansys 远程求解管理器 (RSM):分步指南
  • 【计算机网络入门】初学计算机网络(七)
  • 算力100问☞第66问:如何降低大模型的训练成本?
  • 计算机网络:自顶向下方法——第四、五章 网络层
  • MySQL中的行级锁
  • 【git】【rebase】git修改提交信息的几种方法
  • 使用IDEA如何隐藏文件或文件夹
  • D033 neo4j知识图谱在线学习系统vue+django+neo4j【单课程】
  • 红锁如何解决分布式锁集群部署下的问题
  • 海康威视摄像头ISUP(原EHOME协议) 摄像头实时预览springboot 版本java实现,并可以在浏览器vue前端播放(附带源码)
  • 计算机视觉(opencv-python)之图像预处理基本操作(待补充)
  • 笔试练习day11
  • 【大模型】Windows桌面版AnythingLLM安装与配置教程
  • Docker网络模式实战
  • 大白话css第六章深入探索前沿技术、性能极致优化以及参与社区与知识沉淀
  • 广州安全教育平台登录账号/优化营商环境
  • 仙游哪里可以做网站的/上海短视频seo优化网站
  • 合肥大型网站建设公司/西安百度快照优化
  • 兰州网站建设q.479185700棒/自动发帖软件
  • 独立外贸网站建设/百度帐号登录个人中心
  • 阿里巴巴怎么做企业网站/市场推广方案模板