首页 > 其他分享 >特殊类设计

特殊类设计

时间:2024-09-26 12:22:01浏览次数:6  
标签:Singleton 特殊 对象 创建 static 单例 设计 拷贝

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

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

C++98的方式(只声明)

将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。

原因:

1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不 能禁止拷贝了

2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写 反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

C++11的方式(=delete)

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上

=delete,表示让编译器删除掉该默认成员函数。

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

实现方式:

1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。

2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

new的需要手动delete,其他两个会自动释放

解决办法1

直接把析构私有,让hp1和hp3无法自动析构

不过会引发hp2无法手动释放

解决办法

解决办法2

这样就会有可以拷贝hp2的情况(拷贝后可能在栈)

解决办法

把拷贝构造和赋值封死

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

方法:

同上将构造函数私有化,然后设计静态方法创建对象返回即可。

因为这里的拷贝也符合了,所以就会出现以下问题(就可以在堆上开空间了)

重要知识点

解决办法:要先了解new的原理,这里new的时候不可以调用构造,但是还可以调用拷贝构造,但这里不能直接封了拷贝构造,因为StackOnly st用的是拷贝构造,new:会调用两个部分,一部分是构造一部分是operator new,之所以要调用operator new,而不直接调用malloc,是因为operator new会抛异常

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

C++98方式

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承

class NonInherit

{

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

private:
 NonInherit()
 {}
};

C++11方法(final)

final关键字,final修饰类,表示该类不能被继承。

class A  final

{
    // ....

};

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

设计模式:

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的 总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打 仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后 来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

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

单例模式:

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

饿汉模式

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

// 饿汉模式

// 优点:简单

// 缺点:可能会导致进程启动慢(都不知道是在初始化还是系统挂了),且如果有多个单例类对象实例启动顺序不确定。

第一步:构造函数私有(不能随意创建)

把map<string,string>设为私有

创建static变量

 类外声明

2、提供获取单例对象的接口函数

创建对象

问题(需要防止拷贝)

然后就会出现一个问题,没有绝对防死可以拷贝构造对象

Singleton copy(Singleton::GetInstance());

第三步防拷贝


添加数据并打印

 饿汉模式:一开始(main函数之前)就创建单例对象
静态的在main之前就创建了
 1、如果单例对象初始化内容很多,影响启动速度
 2、如果两个单例类,互相有依赖关系。 
 假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A
class Singleton
{
public:
	// 2、提供获取单例对象的接口函数
	static Singleton& GetInstance()//instance是实例的意思
	{
		return _sinst;
	}

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

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

private:
	// 1、构造函数私有
	Singleton()
	{
		// ...
	}

	// 3、防拷贝
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton& s) = delete;

	//使map全局只有唯一实例
	map<string, string> _dict;
	// ...

	//可以创建自己类型的对象,也不会套娃,因为静态的在静态区
	//静态的在main之前就创建了
	static Singleton _sinst;
};

Singleton Singleton::_sinst;

int main()
{
	//保证创建的都是一个对象
	//三个地址都一样,说明三个创建的是同一个
	cout << &Singleton::GetInstance() << endl;
	cout << &Singleton::GetInstance() << endl;
	cout << &Singleton::GetInstance() << endl;

	Singleton copy(Singleton::GetInstance());

	//添加数据
	Singleton::GetInstance().Add({ "1111","2222" });
	Singleton::GetInstance().Print();
	return 0;
}

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

大问题用懒汉解决

1、如果单例对象初始化内容很多,影响启动速度
2、如果两个单例类,互相有依赖关系。 
假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A,就没法用饿汉了,然后就有了下面的懒汉

懒汉模式

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

// 懒汉

// 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控 制。

// 缺点:复杂

//懒汉用的static指针,main之前创建一个指针不耽误时间
//顺序可以随便控制
//单例一般不用智能指针,所以需要显示释放
// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)

改进


//懒汉用的static指针,main之前创建一个指针不耽误时间
//顺序可以随便控制
//单例一般不用智能指针,所以需要显示释放
// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)

namespace lazy
{
	class Singleton
	{
	public:
		//改进::2
		// 2、提供获取单例对象的接口函数
		static Singleton& GetInstance()
		{
			if (_psinst == nullptr)
			{
				// 第一次调用GetInstance的时候创建单例对象
				_psinst = new Singleton;
			}

			return *_psinst;
		}

		//改进::3
		// 一般单例不用释放。
		// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)
		static void DelInstance()
		{
			if (_psinst)
			{
				delete _psinst;
				_psinst = nullptr;
			}
		}

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

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

		//改进::5,因为一个单例显示释放一次多个就要多次delete太麻烦
		//所以有了GC
		//用智能指针又没办法显示释放
		class GC
		{
		public:
			~GC()
			{
				lazy::Singleton::DelInstance();
			}
		};

	private:
		// 1、构造函数私有
		Singleton()
		{
			// ...
		}
		
		//程序结束后,把东西写入文件中(可持久化)
		~Singleton()
		{
			cout << "~Singleton()" << endl;

			//改进::4
			// 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);
			}
		}

		// 3、防拷贝
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;

		map<string, string> _dict;
		// ...

		//改进::这里弄一个指针
		static Singleton* _psinst;

		//改进::5
		static GC _gc;
	};
	//类外定义
	Singleton* Singleton::_psinst = nullptr;
	//改进::5
	Singleton::GC Singleton::_gc;
}
int main()
{
	//Singleton s1;
	//Singleton s2;

	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;

	//Singleton copy(Singleton::GetInstance());

	lazy::Singleton::GetInstance().Add({ "xxx", "111" });
	lazy::Singleton::GetInstance().Add({ "yyy", "222" });
	lazy::Singleton::GetInstance().Add({ "zzz", "333" });
	lazy::Singleton::GetInstance().Add({ "abc", "333" });

	lazy::Singleton::GetInstance().Print();

	//lazy::Singleton::DelInstance();

	lazy::Singleton::GetInstance().Add({ "abc", "444" });
	lazy::Singleton::GetInstance().Print();

	//lazy::Singleton::DelInstance();

	return 0;
}

标签:Singleton,特殊,对象,创建,static,单例,设计,拷贝
From: https://blog.csdn.net/2401_83427936/article/details/141611420

相关文章

  • 建投数据获得安防工程企业设计施工维护能力证书(壹级)
    近日,经中国安全防范产品行业协会审核,建投数据顺利获得安防工程企业设计施工维护能力证书(壹级)。安防工程企业设计施工维护资质是对企业安防工程设计、施工、维护等领域专业能力的综合认证,是衡量企业规模、工程业绩、管理水平、诚信表现的重要指标。其评定标准是根据建筑安全防范管理......
  • 基于springboot的企业人事管理系统的设计与实现 (含源码+sql+视频导入教程+论文)
    ......
  • 基于springboot和vue的教务学生选课管理系统的设计与实现 (含源码+sql+视频导入教程)
    ......
  • 基于Java+SpringBoot+Mysql陪练系统功能设计与实现一
    一、前言介绍:1.1项目摘要随着互联网技术的飞速发展和普及,网络游戏已成为人们休闲娱乐的重要方式之一。随着游戏玩家群体的不断扩大,游戏内竞争日益激烈,许多玩家为了提升游戏技能、快速升级或享受游戏乐趣,开始寻求外部帮助,如寻找高水平玩家进行陪玩或雇佣代练服务。这一需......
  • 基于Java+SpringBoot+Mysql陪练系统功能设计与实现二
    一、前言介绍:1.1项目摘要随着互联网技术的飞速发展和普及,网络游戏已成为人们休闲娱乐的重要方式之一。随着游戏玩家群体的不断扩大,游戏内竞争日益激烈,许多玩家为了提升游戏技能、快速升级或享受游戏乐趣,开始寻求外部帮助,如寻找高水平玩家进行陪玩或雇佣代练服务。这一需......
  • jsp电影网站设计6n6p9
    jsp电影网站设计6n6p9本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能用户,电影分类,电影信息技术要求:   开发语言:JSP前端使用:HTML5,CSS,JSP动态网页技术后端使用SpringBoot,Spring技术主数......
  • jsp地铁运营管理系统设计与实现5372j
    jsp地铁运营管理系统设计与实现本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能公告类型,公告信息,部门,员工,工作类型,分配工作,设备信息,设备安排,用户,问题反馈,线路信息,购票订单技术要求:  ......
  • Leetcode 622. 设计循环队列
    1.题目基本信息1.1.题目描述设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列......
  • Leetcode 706. 设计哈希映射
    1.题目基本信息1.1.题目描述不使用任何内建的哈希表库设计一个哈希映射(HashMap)。实现MyHashMap类:MyHashMap()用空映射初始化对象voidput(intkey,intvalue)向HashMap插入一个键值对(key,value)。如果key已经存在于映射中,则更新其对应的值value。intget(in......
  • 【开题报告+文档+源码】基于SpringBoot的占航快递服务管理系统的设计与实现
    项目背景与意义随着电子商务的迅猛发展和人们生活水平的提高,快递服务行业正经历着前所未有的增长。占航快递公司作为国内知名的快递企业之一,面临着巨大的机遇和挑战。传统的快递服务管理方式已经无法满足日益增长的业务需求,快递服务流程中的问题逐渐凸显,如信息不透明、配送效......