「C/C++ 01」 深拷贝和浅拷贝

news2025/2/1 12:05:10

目录

一、概念 

1. 浅拷贝

2. 深拷贝

3. 深浅拷贝问题

4. 总结 

二、在C++的类中实现深拷贝

1. 拷贝构造函数 中实现深拷贝

a. 自己开辟一个新空间,然后将内容拷贝到新空间

b. 借助构造函数来实现深拷贝

2. operator= 中实现深拷贝 

a. 自己开辟一个新空间,然后将内容拷贝到新空间

b. 借助构造函数来实现深拷贝


一、概念 

1. 浅拷贝

a. 是什么?

        浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象成员属性值的一份精确拷贝。如果成员是基本类型,拷贝的就是基本类型的值;如果属性是指针类型,拷贝的就是内存地址(即原来的指向) ,因此如果其中一个对象改变了指针指向的区域,就会影响到另一个对象。

        在浅拷贝中,只是复制了对象的引用或指针,而不是实际的数据。这意味着原始对象和复制的对象都指向同一块内存区域。因此,如果修改了其中一个对象,另一个对象也会受到影响。


b. 怎么做?

        在C语言中,如果使用赋值运算符(=)来完成结构体的拷贝就是浅拷贝;在C++中编译器默认生成的拷贝构造函数和赋值运算符重载函数都是使用浅拷贝。

2. 深拷贝

a. 是什么?

        深拷贝则完全复制了对象的数据,创建了一个全新的、独立的副本。修改其中一个对象不会影响另一个对象。


b. 怎么做?

        实现深拷贝的关键是对于原结构体或原对象中的每一个指针成员,都开辟一个新空间,然后再将原空间的内容拷贝到新空间中,然后让新结构体/对象中的指针成员指向这个新空间。

3. 深浅拷贝问题

        深浅拷贝问题主要是出现在对象或结构体的拷贝过程中。这个问题不仅仅出现在C/C++中,Java、Python等也有类似的问题。

        在C/C++中,结构体/对象的拷贝通常使用赋值运算符(=)来完成。如果结构体中包含指针,那么默认的拷贝操作是浅拷贝,通过上面的讲解可知如果直接用=来创建结构体/对象的拷贝,如果其一个结构体/对象对指针成员指向的内容进行修改就会影响到其他的结构体/对象的拷贝。因此在创建结构体/对象的拷贝需要使用深拷贝,需要手动实现数据的复制。

4. 总结 

       以代码仓库为例:浅拷贝就像是给原始代码仓库添加一个管理者让他也能操作这个仓库,但是,如果原始仓库被删除,那么所有人都无法访问这个仓库;深拷贝就是新建一个仓库,然后把原始仓库的内容拷贝到里面,这样把自己的仓库删掉了也不影响别人的仓库。


       所以实际的编程中,深拷贝通常需要更复杂的实现,因为它需要创建新的内存空间来存储复制的内容。而浅拷贝则相对简单,因为它只是复制了指针或引用,而不是实际的数据内容。


二、在C++的类中实现深拷贝

        实现深拷贝的关键是自己开辟一个新空间,然后再将原空间的内容拷贝到新空间。以下以stack类为例,演示如何实现深拷贝。

1. 拷贝构造函数 中实现深拷贝

a. 自己开辟一个新空间,然后将内容拷贝到新空间

b. 借助构造函数来实现深拷贝。

       在构造函数中自然要动态成员变量开辟空间,所以在拷贝构造函数中可以使用构造函数创建一个临时对象,然后交换对象和临时对象的动态成员


       但这有个小问题,就是当前对象未初始化,直接交换数值可能会导致程序崩溃,所以加上初始化列表,在交换数值前先初始化。

2. operator= 中实现深拷贝 

a. 自己开辟一个新空间,然后将内容拷贝到新空间

       如果不重载,依靠编译器自动生成的 operator=,还是浅拷贝。(析构时仍会出现“双重删除”问题)


以下将一步一步在operator= 中实现标准的深拷贝 :

错误一:更改指针指向前一定要释放原空间,不然会造成内存泄漏。

错误二:上面样写没有考虑到自己给自己赋值的问题,所以还要加一个判断。

错误三:还没有考虑new失败的问题。(需要的内存过大可能会导致new失败)

按照上面的写法,new失败后,不但没有成功给str赋值,str原来的内容也被销毁了。

这里我们先开辟空间给一个临时变量,这样如果new失败了,原来的str也还没有被销毁。

最后再给new的失败加一个提示或抛异常。

b. 借助构造函数来实现深拷贝。

        当然也可以像拷贝构造一样,借助构造函数构造一个临时变量,再彻底交换,临时变量结束时也会调用析构函数释放原有的空间。(注,这里的swap是需要自己实现的对象交换函数)


【源代码】 

#include<iostream>
using namespace std;


class stack
{
public:
	int* _arr;
	// 构造函数
	stack(int* arr = nullptr)
	{
		_arr = new int();
		int len = sizeof(arr) / sizeof(arr[0]);
		copy(arr, arr + len, _arr);
	}
	~stack()
	{
		cout << "删除地址为:" << _arr << "的数组。" << endl;
		delete(_arr);
	}
	 1. 自己开辟一个新空间,然后将内容拷贝到新空间。
	//stack(const stack &s)
	//{
	//	//开辟新空间
	//	_arr = new int();
	//	//拷贝内容
	//	int len = sizeof(s._arr) / sizeof(s._arr[0]);
	//	copy(s._arr, s._arr + len, _arr);
	//}

	 2. 借助构造函数创建中间对象来实现深拷贝。
	//stack(const stack& s)
	//{
	//	//调用构造函数创建中间对象(系统会开辟好空间)
	//	stack tmp(s._arr);
	//	swap(_arr, tmp._arr);
	//}
	 但这有个小问题,就是当前对象未初始化,
	 直接交换数值可能会导致程序崩溃,所以加上初始化列表,在交换数值前先初始化。
	stack(const stack& s)
		:_arr(nullptr)
	{
		//调用构造函数创建中间对象
		stack tmp(s._arr);
		swap(_arr, tmp._arr);
	}

	stack& operator= (const stack& s)
	{
		// 自己给自己赋值时无需开辟新空间。
		if (this != &s)
		{
			//调用构造函数创建中间对象(系统会开辟好空间)
			stack tmp(s._arr);
			swap(_arr, tmp._arr);
		}
		return *this;
	}
};

int main()
{
	int* arr = new int[5];
	arr[0] = 0;
	stack st1(arr);
	stack st2 = st1;
	cout << "st1数组的地址:" << st1._arr << endl \
		 << "st2数组的地址:" << st2._arr << endl;
	return 0;
}


------------------------END-------------------------

才疏学浅,谬误难免,欢迎各位批评指正。

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

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

相关文章

JAVA版的鸿鹄云商B2B2C:多商家入驻直播商城系统特性解析 商城免 费搭建

鸿鹄云商 b2b2c产品概述 【b2b2c平台】&#xff0c;以传统电商行业为基石&#xff0c;鸿鹄云商支持“商家入驻平台自营”多运营模式&#xff0c;积极打造“全新市场&#xff0c;全新 模式”企业级b2b2c电商平台&#xff0c;致力干助力各行/互联网创业腾飞并获取更多的收益。从消…

pyqt5实现wget下载视频文件的进度条显示

简介&#xff1a; 最近在写一个项目&#xff0c;用到了wget下载视频&#xff0c;为了更好的视觉效果&#xff0c;所以使用pyqt5中QProgressBar来实现下载进度条。当视频开始下载就会弹出下载进度条&#xff0c;下载完成后进度条消失。效果如下图; 具体代码实现 &#xff1a; …

Sobit:将BRC20资产桥接到Solana ,加速铭文市场的火热

2023 年 1 月份后&#xff0c;比特币 Ordinals 协议出现后为铭文赛道的出现奠定了基础&#xff0c;它以聪为单位将比特币分成份&#xff0c;并在每一聪上攥刻不同的信息以达到非同质化资产的效果&#xff0c;而此后包括 BRC20 在内的诸多采用了 Ordinals 方案的应用不断面向市场…

生成式AI大模型对人类进化的影响

你是不是发现每天的工作都离不开ChatGPT之类的语言生成模型&#xff1f;离不开类似Midjourney的图像生成模型&#xff1f;离不开一些设计类的AI辅助工具&#xff1f;如果是&#xff0c;那说明你已经逐步被AI侵蚀了&#xff0c;你的创造力也正在逐渐下降&#xff0c;大模型正在剥…

使用通道和模式

通过通道、选择语句和最佳实践掌握 Go 中的并发编程 并发编程是构建高效和响应迅速的软件的强大范例。Go&#xff0c;也被称为 Golang&#xff0c;通过通道提供了一种健壮且优雅的解决方案来进行并发通信。在这篇文章中&#xff0c;我们将探讨通道的概念、它们在并发编程中的作…

Qt配置opencv,cmake编译参考笔记,已成功

Qt版本&#xff1a;Qt5.14.2 opencv&#xff1a;4.5.4&#xff08;不要用4.5.5&#xff01;&#xff01;很坑别问我为什么知道&#xff09; cmake&#xff1a;下的最新版本 前言&#xff1a;为什么非得要用cmake编译呢&#xff1f;跳过cmake不好吗&#xff1f; 之前用的opencv…

pickle反序列化

文章目录 基础知识pickle简介可序列化对象object.__reduce__() 函数 pickle过程详细解读opcode简介pickletools 漏洞利用利用思路如何手写opcode 工具pker实战例题[MTCTF 2022]easypickle[HZNUCTF 2023 preliminary]pickle 基础知识 pickle简介 与PHP类似&#xff0c;python也…

内存管理学习

内存管理 在计算系统中&#xff0c;通常存储空间分为两种&#xff1a;内部存储空间和外部存储空间。 内部存储空间通常访问速度比较快&#xff0c;能够按照变量地址随机访问&#xff0c;也就是我们通常所说的RAM&#xff08;随机存储器&#xff09;&#xff0c;可以把它理解为…

两种方法解决win10开机慢,经验分享

方法一&#xff1a; 1、按快捷键“winR”打开 运行窗口。 2、这时候输入“msconfig”后 &#xff0c;点击“确定”或者按“ENTER”键。 3、这时候会打开一个名为“系统配置”的窗口&#xff0c; 在“常规”选项框下 勾选“有选择的启动”下的“加载系统服务”和“加载启动项”。…

Python的环境搭建环境配置()

Python 环境搭建 一,下载Python 1.去官网 www.python.org 下载环境 2.如图点击Download 3.选择Windows 4.如图直接下载 5.直接勾选 6.后面就一直默认选项 Win11 安装目录 不能放在C盘的ProgramFIle路径下 二,测试环境是否安装成功 1.winR 输入cmd 2.输入python --versio…

[已解决] Ubuntu远程桌面闪退+登录显示“远程桌面由于数据加密错误 , 这个会话将结束“

两个月前&#xff0c;由于跑代码在Ubuntu配置环境&#xff0c;乱七八糟的下载了很多东西&#xff0c;导致了一系列问题..... 问题1 Ubuntu远程桌面闪退 实验室有两台服务器&#xff0c;IP后三位分别为141和142&#xff0c;其中141在输入密码后立即闪退&#xff0c;142可以正常…

110基于matlab的混合方法组合的极限学习机和稀疏表示进行分类

基于matlab的混合方法组合的极限学习机和稀疏表示进行分类。通过将极限学习机&#xff08;ELM&#xff09;和稀疏表示&#xff08;SRC&#xff09;结合到统一框架中&#xff0c;混合分类器具有快速测试&#xff08;ELM的优点&#xff09;的优点&#xff0c;且显示出显着的分类精…

华为鸿蒙开发适合哪些人学习?

随着鸿蒙系统的崛起&#xff0c;越来越多的人开始关注鸿蒙开发&#xff0c;并希望成为鸿蒙开发者。然而&#xff0c;鸿蒙开发并不适合所有人&#xff0c;那么哪些人最适合学习鸿蒙开发呢&#xff1f;本文将为您总结鸿蒙开发适合的人群。 一、具备编程基础的人 学习鸿蒙开发需要…

python报错A value is trying to be set on a copy of a slice

加入.copy()即可避免该报错提示 原代码&#xff1a; df5df4.drop_duplicates() print(df5.shape)df5[班型中文名称]df5[班型名称]-A print(df5.head()) 输出结果&#xff1a; 修改后代码&#xff1a; df5df4.drop_duplicates().copy() print(df5.shape)df5[班型中文名称]df…

MyBatis关联查询(三、多对多查询)

MyBatis关联查询&#xff08;三、多对多查询&#xff09; 需求&#xff1a;查询角色及角色赋予的用户信息。 分析&#xff1a;一个用户可以拥有多个角色&#xff0c;一个角色也可以赋予多个用户&#xff0c;用户和角色为双向的一对多关系&#xff0c;多对多关系其实我们看成是…

2. 行为模式 - 命令模式

亦称&#xff1a; 动作、事务、Action、Transaction、Command 意图 命令模式是一种行为设计模式&#xff0c; 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中&#xff0c; 且能实现可撤销…

SVM —— 代码实现

SMO 算法的实现步骤&#xff1a; 代码如下&#xff1a; import numpy as np import matplotlib.pyplot as plt import seaborn as sns import random# 设置中文字体为宋体&#xff0c;英文字体为 times new roman sns.set(font"SimSun", style"ticks", fo…

从零开始创建GPTs 人人都可以编写自己的ChatGPT产品

在这个人工智能迅猛发展的时代&#xff0c;GPT&#xff08;生成式预训练变换器&#xff09;已经成为一项令人兴奋的技术&#xff0c;它打开了创意和知识的新大门。无论你是一名编程新手、一位热爱探索的学生&#xff0c;还是对未来充满好奇的专业人士&#xff0c;GPTs都可以为你…

Win7如何修改MAC地址

MAC地址&#xff0c;又叫做物理地址、硬件地址&#xff0c;是用来定义网络设备的位置&#xff0c;一般情况下&#xff0c;MAC地址在网卡中是固定的&#xff0c;但不排除有人手动去修改自己的MAC地址。win7如何修改MAC地址?其实修改MAC地址的方法很简单&#xff0c;可以通过硬件…

【经典LeetCode算法题目专栏分类】【第8期】滑动窗口:最小覆盖子串、字符串排列、找所有字母异位词、 最长无重复子串

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…