浅谈C++的浅拷贝和深拷贝问题

今天我们来谈谈C++的浅拷贝和深拷贝问题,这里先上定义,可以直接浏览下面的表格,比较直观😊😊😊 。在C++中,浅拷贝和深拷贝是两种对象复制的方式,其中🐱浅拷贝(Shallow Copy)是指将一个对象的指赋值到另一个对象中,但只赋值对象的成员变量的值,并不对复制对象的动态分配内存(如堆内存)等外部资源,这也就意味着当原对象修改自己指向的外部资源时,可能会影响到另一个对象;🐶深拷贝(Deep Copy)是指在复制对象时,不仅复制对象中的值,还复制指向动态分配内存的指针所指向的内存。这样每个对象都有自己的独立内存,它们之间不会相互干扰。自定义的复制构造函数和赋值运算符通常会进行深拷贝。

对象复制的方式定义
🐱浅拷贝(Shallow Copy)将一个对象的指赋值到另一个对象中,但只赋值对象的成员变量的值,并不对复制对象的动态分配内存(如堆内存)等外部资源
🐶深拷贝(Deep Copy)将一个对象的值复制给另一个对象,包括了对象成员变量的值以及对象的动态分配内存(如堆内存)外部资源

从上述定义中我们可以想到,要是通过浅拷贝的方式拷贝对象,当发生原对象修改自己指向的外部资源时,由于另一个对象并不会复制原对象的外部资源,而是与原对象指向同一块外部资源,如图所示:
在这里插入图片描述
因此在程序结束调用析构函数时,很可能会对指向的外部资源进行二次析构而导致系统崩溃,下面给出一段错误代码🫤:

#include <iostream>
using namespace std;

class SeqStack
{
public:
	SeqStack(int size = 5)
	{
		cout << this << " SeqStack() " << endl;
		_pstack = new int[size];
		_top = -1;
		_size = size;
	}
	~SeqStack() 
	{
		cout << this << " ~SeqStack() " << endl;
		delete[] _pstack;
		_pstack = nullptr;
	}
	
private:
	int* _pstack;
	int _top;
	int _size;
};

int main()
{
	SeqStack s;  //没有提供任何构造函数时,编译器会提供默认构造和析构函数
	SeqStack s1(5);  
	SeqStack s2 = s1; // #1  拷贝构造函数,做内存拷贝
	//SeqStack s3(s1); #2 = #1

	return 0;
}

根据上述描述,我们可以推导出,在程序准备进行析构时,会先调用 s2 的析构函数(释放*_pstack),此时*_pstack变成了空指针,再进行s1的析构,由于它们指向同一块外部资源,s1的析构会导致系统崩溃💦💦💦,结果如下:
在这里插入图片描述
因此在对象成员有开辟了外部资源的前提下,我们进行拷贝构造时需要自定义构造函数来避免出现此类问题,下面为自定义拷贝构造函数的代码:

//自定义拷贝构造函数
SeqStack(const SeqStack& src)
{
	_pstack = new int[src._size];
	for (int i = 0; i <= src._top; i++)  
		_pstack[i] = src._pstack[i];
		
	//memcpy() realloc()
	_top = src._top;
	_size = src._size;
}

加上这段代码,此时就会在复制对象时新开辟一块独立于原对象的外部资源,做了一次深拷贝,防止出现浅拷贝问题(二次析构),猜猜现在的程序是否能够正常运行呢?

🤔🤔🤔思考一下:为什么我们要自定义拷贝构造函数使用for循环而不直接使用 memcpy 或者 realloc 函数来进行拷贝呢?

同理

  • memcpy函数只是简单的将一块内存中的内容复制到另一块内存中,不会处理动态内存的分配和释放,也不会对对象中的成员进行初始化和析构, 会导致指针成员指向同一块内存。
  • realloc函数用于重新分配已经分配的动态内存,它会尝试扩大或缩小内存块的大小,同时保持原有内存块中的数据不变。虽然在需要调整动态内存大小的时候非常有用,但是它并不能正确地处理C++对象的构造和析构。

我们再来看看一个简单的赋值操作,可以在main函数中在 s2 做完深拷贝之后,加一条s2 = s1语句,如下:

int main()
{
	SeqStack s;  //没有提供任何构造函数时,编译期会提供默认构造和析构函数
	SeqStack s1(5);   // #1  拷贝构造函数,做内存拷贝
	SeqStack s2 = s1; // #2
	
	s2 = s1;  //这里!!!!
	return 0;
}

看看运行结果,令人发指 😧 😧 😧

在这里插入图片描述

这块的话其实也是浅拷贝问题的一种,因为我们没有在类中自定义赋值函数,C++编译器会调用它的默认赋值函数,做的也是数据的内存拷贝(即浅拷贝),直接把对象s2*_pstack指向了对象s1*_pstack,而原先对象s2*_pstack所指向的另一块外部资源被丢弃了,使之无法释放,如下同所示:
在这里插入图片描述
于是乎我们想到🤔,还要得重载一下赋值函数,同时释放那块即将被丢弃的外部资源:

//重载赋值函数
void operator=(const SeqStack& src) //大致跟拷贝构造函数类似
{
	cout << "operatot=" << endl;
	//if (this == &src) return; //防止自赋值 s1 = s1 而引起非法访问

	delete[]_pstack; //需要把原来指向的外部资源释放

	_pstack = new int[src._size];
	for (int i = 0; i <= src._top; i++)  _pstack[i] = src._pstack[i];
	_top = src._top;
	_size = src._size;
}

我们常说理论指导实践,在看完上面的论述后,将给出一个应用实例作为练习,需要实例代码的话可以私信我😊
在这里插入图片描述
哦对,这里

最后来做个总结吧: 概念总结

🐱 浅拷贝:

  • 只复制对象的成员变量的值,不复制动态分配的资源。
  • 原对象和副本对象共享同一块堆内存。
  • 修改其中一个对象会影响到另一个对象。

🐶 深拷贝:

  • 复制对象的成员变量的值以及动态分配的资源。
  • 原对象和副本对象拥有各自独立的资源。
  • 修改其中一个对象不会影响到另一个对象。

🌻🌻🌻以上就是有C++浅拷贝和深拷贝的有关问题,如果聪明的你浏览到这篇文章并觉得文章内容对你有帮助,请不吝动动手指,给博主一个小小的赞和收藏 🌻🌻🌻

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

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

相关文章

最近很火的“30秒展现女孩的一生”AI视频制作全流程

最近很火的AI视频&#xff0c;30秒展现一个人的一生&#xff0c;我们来看一下制作流程 包含以下知识点 安装ComfyUI AnimeDiff插件使用Prompt travel描述视频画面 加载工作流文件 在BatchPromptSchedule这个节点里描述出画面的内容&#xff0c;前面的数字代表对应的帧数 &q…

损失函数和反向传播

1. 损失函数的基础 import torch from torch.nn import L1Loss from torch import nninputs torch.tensor([1, 2, 3], dtypetorch.float32) targets torch.tensor([1, 2, 5], dtypetorch.float32)inputs torch.reshape(inputs, (1, 1, 1, 3)) targets torch.reshape(targe…

[已解决]安装CUDA失败报错(附万能解决办法)

[已解决]安装CUDA失败报错(附万能解决办法) &#xff08;Tips&#xff1a;赶时间直接看万能法2&#xff09; 经过长时间的尝试和研究&#xff0c;我终于解决了安装CUDA失败报错的问题。在这里&#xff0c;我将记录下我遇到的问题以及解决办法&#xff0c;希望对其他小白们有所帮…

关于PolarDB粗浅认识

PolarDB简介 目前&#xff08;20240314&#xff09;&#xff0c;PolarDB有两个版本&#xff1a; PolarDB-PG PolarDB PostgreSQL 版&#xff08;PolarDB for PostgreSQL&#xff0c;简称“PolarDB-PG”&#xff09;是阿里云自主研发的云原生关系型数据库产品&#xff0c;100%…

el-dialog弹框遮罩层问题

先看一下出现的bug&#xff0c;点击按钮出现弹框的时候&#xff0c;遮罩层出现在弹框上层&#xff0c;不知道是那个同事写的全局样式影响的&#xff0c;这个时候我们需要在el-dialog标签上添加一个属性就行 :modal-append-to-body“false” 下图是出现的问题&#xff0c;遮罩层…

什么是 HTTPS?它是如何解决安全性问题的?

什么是 HTTPS&#xff1f; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是一种安全的通信协议&#xff0c;用于在计算机网络上安全地传输超文本&#xff08;如网页、图像、视频等&#xff09;和其他数据。它是 HTTP 协议的安全版本&#xff0c;通过使用加…

【微服务学习笔记(一)】Nacos、Feign、Gateway基础使用

【微服务学习笔记&#xff08;一&#xff09;】Nacos、Feign、Gateway基础使用 总览Nacos安装配置Nacos注册中心服务多级存储模型负载均衡规则环境隔离 配置管理配置拉取配置热更新多服务共享配置 Feign远程调用配置性能优化Fegin使用 统一网关Gateway搭建网关路由断言工厂&…

扫雷小游戏制作教程:用HTML5和JavaScript打造经典游戏

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

ASP.NET-Server.UrlEncode

目录 背景: Server.UrlEncode作用: 1.URL 编码&#xff1a; 2.避免冲突&#xff1a; 3.安全性&#xff1a; 4.规范化&#xff1a; 实例说明: 不使用Server.UrlEncode 使用Server.UrlEncode 总结: 背景: Server.UrlEncode方法在ASP.NET中主要功能是对URL中的参数进行编…

使用点链云管家创建瑜伽约课小程序

点链云管家 点链云管家是由上海点链科技开发的门店管理系统&#xff0c;为线下门店商家提供一站式门店运营服务平台解决方案&#xff0c;适用于瑜伽健身、美业、新零售会员制电商、母婴店、宠物店、按摩养生、服装、美容、美甲、汽车服务、商超零售、餐饮、KTV娱乐、干洗等18个…

CodeFuse代码优化实战:Java日期格式化时如何正确表示年份?

代码优化&#xff0c;是 CodeFuse 插件推出的功能之一&#xff0c;它可以对选定代码段进行分析理解&#xff0c;提出优化和改进建议。还能直接基于改进建议形成代码补丁&#xff0c;帮助开发者写出更好的代码。 安装CodeFuse插件后&#xff0c;选中代码右键即可使用代码优化功能…

技术帖 | 飞凌嵌入式AM62x核心板SPI的详解与应用

SPI&#xff08;Serial Peripheral Interface&#xff09;通信总线以其高速、全双工、同步的特性而被广泛应用&#xff0c;它只需要四根线就能实现数据传输&#xff0c;有效地节约了芯片管脚的数量&#xff0c;同时为PCB布局带来了空间上的优化和便捷。正因为它简单易用的特点&…

csp模拟题(201604-2,俄罗斯方块模拟下坠)

题目 问题描述 俄罗斯方块是俄罗斯人阿列克谢帕基特诺夫发明的一款休闲游戏。   游戏在一个15行10列的方格图上进行&#xff0c;方格图上的每一个格子可能已经放置了方块&#xff0c;或者没有放置方块。每一轮&#xff0c;都会有一个新的由4个小方块组成的板块从方格图的上方…

NCV7356D1R2G接口集成芯片中文资料PDF数据手册参数引脚图规格书价格图片

产品概述&#xff1a; NCV7356 是一款用于单线数据链路的物理层器件&#xff0c;能够使用多种具碰撞分解的载波感测多重存取 (CSMA/CR) 协议运行&#xff0c;如博世控制器区域网络 (CAN) 2.0 版。此串行数据链路网络适用于不需要高速数据的应用&#xff0c;低速数据可在物理介…

基于RK3588+Codesys+Xenomai的ARM+LINUX实时硬件平台的软PLC解决方案

产品概述 公司推出基于瑞芯微RK3588架构的AI边缘计算主板&#xff0c;RK3588是新一代国产旗舰高性能64位八核处理器&#xff0c;采用8nm工艺&#xff0c;具有高算力、低功耗、超强多媒体、丰富数据接口等特点。搭载四核A76四核A55的八核CPU和ARM G610MP4 GPU&#xff0c;内置6…

css超出部分显示省略号

目录 前言 一、CSS单行实现 二、CSS多行实现&#xff08;CSS3出的&#xff0c;兼容性需要注意&#xff09; 三、微信小程序超过2行出现省略号实现 四、JavaScript脚本实现 前言 CSS文本溢出就显示省略号&#xff0c;就是在样式中指定了盒子的宽度与高度,有可能出现某些内…

STM32基础--中断应用

本文章里面假设中断就是异常&#xff0c;不做区分。 异常类型 F103 在内核水平上搭载了一个异常响应系统&#xff0c;支持为数众多的系统异常和外部中断。其中系统异常有 8 个&#xff08;如果把 Reset 和 HardFault 也算上的话就是 10 个&#xff09;&#xff0c;外部中断有…

百度paddleocr GPU版部署

显卡&#xff1a;NVIDIA GeForce RTX 4070&#xff0c;Nvidia驱动程序版本&#xff1a;537.13 Nvidia驱动程序能支持的最高cuda版本&#xff1a;12.2.138 Python&#xff1a;python3.10.11。试过python3.12&#xff0c;安装paddleocr失败&#xff0c;找不到相关模块。 飞桨版本…

Java优先级队列(堆)

&#x1f435;本篇文章将对优先级队列&#xff08;堆&#xff09;的相关知识进行讲解 一、优先级队列 队列是一种“先入先出”的数据结构&#xff0c;但有时操作的数据带有优先级&#xff0c;需要优先处理&#xff0c;这时普通的队列就不能满足需求。比如&#xff1a;在排队取…

《JAVA与模式》之抽象工厂模式

系列文章目录 文章目录 系列文章目录前言一、使用简单工厂模式的解决方案二、引进抽象工厂模式三、抽象工厂模式结构四、抽象工厂模式的优缺点前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看…