【C++】特殊类实现——设计一个类、不能被拷贝、只能在堆上创建对象、只能在栈上创建对象、不能被继承、单例模式、饿汉模式、懒汉模式

news2024/12/25 1:29:43

文章目录

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

C++

在这里插入图片描述

特殊类实现

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

  在C++中,拷贝构造函数和拷贝赋值运算符是两种可以用于创建新对象或为现有对象赋值的方法。

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

  以下是一个示例,展示如何创建一个不能被拷贝的类:

  C++98做法:

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

class CopyBan
{
    // ...
    
private:
    CopyBan(const CopyBan&);
    CopyBan& operator=(const CopyBan&);
    //...
};

  原因:

  (1)只声明不定义:因为没有定义,所以该函数根本不会进行任何操作,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

  (2)同时设置成私有:这样可以防止直接使用它们, 如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了。

  

  C++11做法:

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

class CopyBan
{
    // ...
    CopyBan(const CopyBan&)=delete;
    CopyBan& operator=(const CopyBan&)=delete;
    //...
};

            

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

  实现方式:

  1.将类的析构函数私有;或者将类的构造函数私有,拷贝构造声明成私有。

  2.提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建(单例模式)。

  

  1.1析构函数私有化:

//析构函数私有化,因为堆要手动释放对象
class HeapOnly
{
public:
	void Destroy()
	{
		delete this;
	}

private:
	~HeapOnly()
	{
		//...
	}
};

int main()
{
	//HeapOnly hp1;
	//static HeapOnly hp2;
	HeapOnly* hp3 = new HeapOnly;
	hp3->Destroy();

	return 0;
}

  原因:

  我们可以将析构函数私有化,因为在栈上和静态区的对象需要自动调用析构函数,而析构函数无法显示调用了,就会导致我们无法在栈上和静态区创建对象。

  因为堆上的对象是需要我们手动的创建和删除的,所以在堆上创建对象只先调用构造函数; 如果我们需要对堆上创建的对象进行销毁,我们可以提供一个公有函数接口,用这个函数接口调用私有函数。

在这里插入图片描述

  

  1.12构造函数私有化:

//2、设计一个类只能在堆上创建对象
//构造函数私有化
class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

private:
	HeapOnly()
	{
		//...
	}

	//防止拷贝构造
	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
};

int main()
{
	//HeapOnly hp1;
	//static HeapOnly hp2;
	//HeapOnly* hp3 = new HeapOnly;
	HeapOnly* hp3 = HeapOnly::CreateObj();
	//HeapOnly copy(*hp3);//拷贝构造在栈上
	return 0;
}

  原因:

  我们将构造函数私有,禁止任何方式创建示例。但是提供一个可以在堆上创建对象的公有函数,这样我们就可以通过公有函数来调用私有的构造函数。

  注意:这里的要创建对象的公有函数应该是static修饰的,因为如果要调用公有函数,需要有一个对象示例,而我们要用公有函数创建一个示例,而我们现在没有对象示例,需要调用公有函数(类似鸡生蛋,蛋生鸡)…如果函数在静态区,就可以直接调用了。

  同时,为了防止我们创建的对象示例被拷贝构造或者赋值,所以我们还需要将拷贝构造函数和赋值运算符重载函数封死。

在这里插入图片描述

            

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

  实现方法:

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

//3、设计一个只能在栈上的类
//构造函数私有
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}
	
private:
	StackOnly()
	{
		//...
	}

	//对一个类实现专属的operator new
	void* operator new(size_t size) = delete;
};

int main()
{
	//StackOnly hp1;
	//static StackOnly hp2;
	//StackOnly* hp3 = new StackOnly;
	StackOnly hp3 = StackOnly::CreateObj();
	StackOnly copy(hp3);

	//new  operator new  +  构造
	//StackOnly* hp4 = new StackOnly(hp3);

	return 0;
}

  原因:

  和上面的实现一样,我们将构造函数私有化,提供一个只能在栈上创建对象的公有函数,static修饰。但是如果我们封死拷贝构造,CreateObj返回的临时对象就无法拷贝给我们的hp3,为了解决我们可以提供一个移动构造

  但是事实上也无法很有效的防止静态区创建对象,所以对于只在栈上创建对象的实现,这样就可以了。

在这里插入图片描述
            

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

  C++98方法:

  我们将构造函数私有化,派生类中调不到基类的构造函数,则无法继承。

//C++98  私有构造函数
class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{
		//...
	}
}

  

  C++11方法:

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

//C++11  final
class A final
{
	//...
};

            

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

  单例模式:

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

5.1饿汉模式

  饿汉模式:

  就是说不管你将来用不用,程序启动时(main函数之前创建)就创建一个唯一的实例对象;

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

  优点:简单;

  缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

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

		void func();

		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<string, string> _dict;
		// ...

		static Singleton _sinst;
	};

	Singleton Singleton::_sinst;

	void Singleton::func()
	{
		// 
		_dict["xxx"] = "1111";
	}
}

  

5.2懒汉模式

  懒汉模式:

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

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

  缺点:复杂。

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

			return *_psinst;
		}

		// 一般单例不用释放。
		// 特殊场景: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;
		}

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

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

		~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);
			}
		}

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

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

		static Singleton* _psinst;
		static GC _gc;
	};

	Singleton* Singleton::_psinst = nullptr;
	Singleton::GC Singleton::_gc;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1189571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11 # 手写 reduce 方法

reduce 使用 reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数&#xff0c;每一次运行 reducer 会将先前元素的计算结果作为参数传入&#xff0c;最后将其结果汇总为单个返回值。 第一次执行回调函数时&#xff0c;不存在“上一次的计算结果”。如果需要回调…

短短45分钟,Open AI撼动了整个AI圈?

相信关注AI行业的人没有人不知道ChatGPT&#xff0c;作为人工智能新产品&#xff0c;ChatGPT一经发出就引爆全球&#xff0c;也让一众企业走上了探索AI大模型之路。而就在国内一众企业就AI大模型不断改进创新时&#xff0c;Open AI用一场仅45分钟的发布会&#xff0c;震惊了整个…

【JavaEESpring】Spring IoCDI

Spring IoC& DI 1. IoC2. IoC & DI 使⽤2.1 Bean的存储2.1 DI 注入 Autowired 3. 练习代码自取 1. IoC Spring 是包含了众多⼯具⽅法的 IoC 容器 IoC: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器。 什么是控制反转呢? 也就…

uniapp使用vue

uniapp集成了Vuex&#xff0c;&#xff0c;并不需要安装vuex 定义自己的vuex vuex中独立命名空间&#xff1a; 可以在模块中使用 namespaced 属性&#xff0c;设置为 true&#xff0c;&#xff0c;这样做的好处是&#xff0c;&#xff0c;不同模块之间的state&#xff0c;mut…

电商库存随笔

好多年没有来写东西了&#xff0c;忙成狗&#xff0c;最近闲暇&#xff0c;有点时间&#xff0c;随手写一下之前的项目中的小点&#xff1b; 一方面是做个总结&#xff0c;一方面打发一下时间 出库 库存扣减时机 下单扣减 [生成订单]付款扣减预扣库存(实际使用) 预扣库存 并…

运营商大数据精准获客:我们提供精准客源渠道的最大资源体?

运营商大数据精准营销 谈起精准获客&#xff0c;竞争对手永远是为我们提供精准客源渠道的最大资源体&#xff01; 最新的获客方式&#xff0c;就是从竞争对手的手中把他们的精准客户资源变为自己的。 今年最火的运营商大数据精准营销是拒绝传统营销方式的烧钱推广&#xff0…

Nginx缓存基础

1 nginx缓存的流程 客户端需要访问服务器的数据时&#xff0c;如果都直接向服务器发送请求&#xff0c;服务器接收过多的请求&#xff0c;压力会比较大&#xff0c;也比较耗时&#xff1b;而如果在nginx缓存一定的数据&#xff0c;使客户端向基于nginx的代理服务器发送请求&…

ChatGPT - 在ChatGPT中设置通用提示模板

文章目录 Prompt设置验证 Prompt VERBOSITY: 我可能会使用 V[0-3] 来定义代码的详细程度&#xff1a;V0 简洁明了 V1 简练 V2 详细 V3 非常详细&#xff0c;附有例子助理回应 您是用户问题背景下的主题专家。我们一步一步来&#xff1a;除非您只是回答一个简短的问题&#xff…

Confluence 漏洞复现(CVE-2023-22515)

Confluence 漏洞复现&#xff08;CVE-2023-22515&#xff0c;CVE-2023-22518&#xff09; 1.CVE-2023-22515权限提升漏洞 1.1漏洞描述 Confluence近期推出的严重漏洞cve-2023-22515&#xff0c;由于未授权和xwork框架问题&#xff0c;导致攻击者可以未授权将系统设置为未安装…

分布式事务的华丽进化

说到分布式事务&#xff0c;大家并不陌生。之前我已做过相关的总结&#xff08;连接附本文后面&#xff09;&#xff0c;不过比较偏理论。在实际工作中&#xff0c;用得比较多的还是柔性分布式事务&#xff0c;今天主要把在工作中运用到的几种柔性分布式事务的场景及实现方式做…

使用nginx作为图片服务器

第一步&#xff1a; 下载nginx版本&#xff0c;去官网下载&#xff0c;这里不建议下载最新版本&#xff0c;因为有时候最新版本还不太稳定。 nginx下载地址官网&#xff1a;nginx: download&#xff0c;然后把下载好的安装包解压出来。 第二步&#xff1a; 在nginx目录下载创建…

在新的服务器上成功安装mysqlclient的方法【解决No matching distribution found for mysqlclient的问题】

前言&#xff1a;在某台Centos服务器上安装mysqlclient时一直报下面的错&#xff1a; WARNING: Discarding https://mirrors.aliyun.com/pypi/packages/6a/91/bdfe808fb5dc99a5f65833b370818161b77ef6d1e19b488e4c146ab615aa/mysqlclient-1.3.0.tar.gz#sha25606eb5664e3738b28…

【2】Spring Boot 3 项目搭建

目录 【2】Spring Boot 3 初始项目搭建项目生成1. 使用IDEA商业版创建2. 使用官方start脚手架创建 配置与启动Git版本控制 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; 【2】Spring Boot 3 初始项目搭建 项目生成 1. 使用IDEA商业版创…

[sd_scripts]之config

https://github.com/kohya-ss/sd-scripts/blob/main/docs/config_README-ja.mdhttps://github.com/kohya-ss/sd-scripts/blob/main/docs/config_README-ja.md[Stable Diffusion]训练你的LoRA(Linux) - 知乎简介LoRA 是一种参数高效微调方法&#xff08;PEFT&#xff09;&#x…

原型链污染漏洞

想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念 可以看这篇文章&#xff1a;对象的继承和原型链 目录 prototype和__proto__分别是什么&#xff1f; 原型链继承 原型链污染是什么 哪些情况下原型链会被污染&#xff1f; 例题1&#xff1a;Code-Breaking 2…

软件测试|PO设计模式在 UI 自动化中的实践

PO的思想最早是2013年由IT大佬Martin Flower提出的&#xff1a;https://martinfowler.com/bliki/PageObject.html 没错&#xff0c;就是他 — 没错&#xff0c;就是他 — 在他的文章里有这样一张经典样图,图片中展示了测试代码中直接操作HTML元素和使用PO模式将page对象封装成…

Android JVM内存模型——老生常谈

jvm简介 JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 jvm作用 Java中的所有类&#xff0c;必须…

Web自动化测试入门篇详解

一、目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”&#xff0c;通常都会作为广大测试从业者的首选学习对象&#xff0c;相较于C/S架构的自动化来说&#xff0c;B/S有着其无法忽视的诸多优势&#xff0c;从行业发展趋、研发模式特点、测试工具支持&…

阿里云e实例服务器3M固定带宽40G ESSD entry系统盘99元/年

阿里云99元服务器新老用户均可以买&#xff0c;你没看错&#xff0c;老用户可以买&#xff0c;活动页面 aliyunfuwuqi.com/go/aliyun 配置为云服务器ECS经济型e实例、2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;并且续费不涨价&#xff0c;原价99元即可续费&#xff0c…

阿里云99元服务器40G ESSD Entry云盘、2核2G3M带宽配置

阿里云99元服务器新老用户均可以买&#xff0c;你没看错&#xff0c;老用户可以买&#xff0c;活动页面 aliyunfuwuqi.com/go/aliyun 配置为云服务器ECS经济型e实例、2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;并且续费不涨价&#xff0c;原价99元即可续费&#xff0c…