【C++】浅拷贝 / 深拷贝 / 写时拷贝

文章目录

  • 1. 经典的string类问题
  • 2. 浅拷贝
  • 3. 深拷贝
    • 3.1 传统写法的String类
    • 3.2 现代写法的String类
  • 4. 写时拷贝

1. 经典的string类问题

上一篇博客已经对string类进行了简单的介绍,大家只要能够正常使用即可。

链接:【C++】string

在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:
	//String()
	//	:_str(new char[1])
	//{
	//	*_str = '\0';
	//}
	
	//String(const char* str = "\0")	// 错误示范
	//String(const char* str = nullptr)	// 错误示范
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

// 测试
void TestString()
{
	String s1("hello string");
	String s2(s1);
}

在这里插入图片描述

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题时,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

2. 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。
在这里插入图片描述
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。
在这里插入图片描述

3. 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数,赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
在这里插入图片描述

3.1 传统写法的String类

class String
{
public:
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}
	
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* pStr = new char[strlen(s._str) + 1];
			strcpy(pStr, s._str);
			delete[] _str;
			_str = pStr;
		}
		return *this;
	}
	
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

3.2 现代写法的String类

class String
{
public:
	String(const char* str = "")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	String(const String& s)
		: _str(nullptr)
	{
		String strTmp(s._str);
		swap(_str, strTmp._str);
	}
	
	// 对比下和上面的赋值那个实现比较好?
	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
	
	//String& operator=(const String& s)
	//{
	//	if (this != &s)
	//	{
	//		String strTmp(s);
	//		swap(_str, strTmp._str);
	//	}
	//	return *this;
	//}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

传统写法就是老老实实自己开空间、自己拷贝数据、自己删除旧空间;而现代写法利用了swap()函数以及局部变量出作用域自动销毁的特性,让函数和编译器帮我们“打工”,我们只要坐享其成即可。这两种方式在效率上并没有什么区别,只是让代码看起来更简洁,但这又会使代码的可读性降低。总体来说,我还是更偏向于传统写法。

4. 写时拷贝

在这里插入图片描述

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数-1,然后再检查是否需要释放资源,如果计数为1,说明该对象是资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象也在使用该资源。


本文完

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用Redis实现实现全局唯一ID

全局唯一ID 如果使用数据库自增ID就存在一些问题: id的规律性太明显受表数据量的限制 全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性: 唯一性高可用递增性安全性高性能 为了增加ID的安全性…

PMP如何续证?

PMP证书的有效期是3年,那过了有效期如何续证呢?对此你需要完成两个步骤: 1.在三年之内向PMI申报完成60个PDU; 2.向PMI申请下一个三年续证的有效期并缴费。 流程看着挺简单,但是对于首次操作的朋友可能会感到有些陌生…

深入解析云原生与Kubernetes:现代化应用开发的未来

云原生是一种创新的软件开发和部署方法论,旨在使应用程序能够更好地适应云环境的特性和需求。而Kubernetes作为云原生的核心技术,提供了强大的容器编排和管理能力,改变了应用部署和运行的方式。本文将深入探讨云原生和Kubernetes的概念、原理…

【VTK三维重建-体绘制】第四期 VTK中GPU加速

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 上期内容讲到VTK的体绘制技术vtkGPUVolumeRayCastMapper,本文分享VTK中GPU加速的相关内容,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞关注,小易会…

WPF Blend for visual studio使用

Blend for visual studio介绍 VS自带Blend for visual studio是专门用来做WPF、Metro等的界面设计的可视化工具,其功能和PS类似。其目的让做界面和后台的程序分开,能快速绘制形状和路径、修改对象样式、动态显示对象(动画)、显示数据等高级操作。VS与Ble…

性能优化-OpenMP基础教程(一)

本文主要介绍OpenMP并行编程技术,编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC&am…

每日一练:LeeCode-LCR 123. 图书整理 I (反转链表)(简)【栈、头插法(虚拟头结点)、双指针、递归】

本文是力扣LeeCode-LCR 123. 图书整理 I (简) 学习与理解过程,本文仅做学习之用,对本题感兴趣的小伙伴可以出门左拐LeeCode。 书店店员有一张链表形式的书单,每个节点代表一本书,节点中的值表示书的编号。为…

漏洞扫描工具scan4all(15000+PoC)

scan4all拥有15000PoC漏洞扫描,23种应用弱口令爆破,7000Web指纹,146种协议,90000规则Port扫描。集成 vscan、nuclei、ksubdomain、subfinder等,充分自动化进行扫描。是一款Fuzz、HW打点、BugBounty神器等工具。 项目地…

Python入门第09篇(conda虚拟环境)

前言 一开始默认安装了最新的Python3.12,搞的倒也顺手,看别人会有不兼容的问题,在我这开始没出现。不过坑总会踩到的,这不就出问题了。pip install一个包一直不行,问了下度娘,说由于这个包使用了一些新技术…

caj转换成pdf有哪些方法?

caj转换成pdf有哪些方法?PDF是一个被广泛支持的文件格式,这种格式基本上在所有的操作系统和设备上都是支持使用的,也能够将PDF文件打开和查看的,相比于caj文件,它就只能通过一下特定的软件或者是插件才能够将caj打开或…

TDD-LTE 寻呼流程

目录 1. 寻呼成功流程 1.1 空闲态寻呼 1.2 连接态寻呼 2. 寻呼失败流程 2.1 Paging消息不可达 2.2 RRC建立失败 2.3 eNodeB未上发Initial UE message或达到超时 1. 寻呼成功流程 1.1 空闲态寻呼 寻呼成功:MME发起寻呼(S1 接口发送Paing 消息&…

【HarmonyOS开发】分布式应用的开发实践(元旦快乐)

元旦快乐,再见2023,加油2024,未来可期,愿新的一年带来健康、幸福和成功!💪 💪💪 多种设备之间能够实现硬件互助、资源共享,依赖的关键技术包括分布式软总线、分布式设备虚…

如何调用FastGPT的API

fastGPT提供兼容OpenAI格式的接口,但是还是有一些地方需要注意 新建一个应用,可以正常测试通过后。【外部使用】【API访问】【新建一个KEY】 我们在调用FastGPT API的时候,需要传递一个chatId的参数,这个是标识同一个会话的参数…

SpringBoot+SSM项目实战 苍穹外卖(08) 用户下单支付订单 内网穿透cpolar软件 绕开微信支付实现

继续上一节的内容,本节导入地址簿功能代码,并实现用户下单和订单支付功能。 这里写目录标题 导入地址簿功能代码接口分析代码实现 用户下单接口分析代码实现 订单支付内网穿透——cpolar软件代码导入绕开微信支付实现 导入地址簿功能代码 地址簿&#x…

注册 Mongodb 官网个人账号

上文 Mongodb基础介绍与应用场景我们简单说了一下 Mongodb 的场景 那么 我们先在他的官网创建一个个人账号 我们先访问官网 https://www.mongodb.com/zh-cn 这里 我们需要注册一下 这里 我们按要求填写信息 然后 点击下面创建账户 然后 点击下面创建账户 然后 他会要求我们邮…

OSCHINA Gitee 联合呈现,《2023 中国开源开发者报告》正式发布,总结分非常帮,可以免费看的报告!

《2023 中国开源开发者报告》 详细地址: https://talk.gitee.com/report/china-open-source-2023-annual-report.pdf 不需要收费下载!! 其中大模型的部分总结的非常棒 gietee 也支持 AI 模型托管了 如何在 Gitee 上托管 AI 模型 https://…

嵌入式视频播放器(mplayer)

1.文件准备: MPlayer-1.0rc2.tar.bz2 libmad-0.15.1b.tar.gz 直接Git到本地 git clone https://gitee.com/zxz_FINE/mplayer_tarball.git 2.文件夹准备: src存放解压后的源码文件,target_Mplayer存放编译安装的目标文件 mkdir src targe…

Ethercat“APWR配置从站地址”报文分析(0x0010:0x0011)

基于IgH主站接了3个从站,分析报文。 APWR:Auto Increment Physical Write。 一条Ethercat报文中可以包含多个子报文,每个子报文的地址由ADPADO组成,ADP即16 bit High Addr,ADO即16 bit Low Addr。APWR模式下&#xff…

git rebase(变基)应用场景

文章目录 git rebase(变基)应用场景1.git rebase -i HEAD~3 git rebase(变基)应用场景 使得提交记录变得简洁 现在我们模拟我们有多次提交记录,本地仓库有三条提交 整合成一条提交记录 1.git rebase -i HEAD~3 提交记录合并 HEAD~3合并三条记录 执行之后 然后把…

Graceful Response 构建 Spring Boot 下优雅的响应处理

一、Graceful Response Graceful Response 是一个 Spring Boot 技术栈下的优雅响应处理器,提供一站式统一返回值封装、全局异常处理、自定义异常错误码等功能,使用Graceful Response进行web接口开发不仅可以节省大量的时间,还可以提高代码质…