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

目录

一、概念 

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.mzph.cn/news/240711.shtml

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

相关文章

One Wire协议应用篇(c语言板)

一.项目简介 利用DS18B20实时检测温度并显示在LCD1602显示屏上&#xff0c;同时可以通过K1,K2,K3,K4设置最高温度和最低温度利用AT24C02可以实现掉电不丢失&#xff0c;最后当检测温度大于或小于最高温时&#xff0c;会在LCD1602显示屏上显示OV:H或OV:L。 二.准备材料 AT89C52、…

ESP32+LVGL笔记(6)-把712k的一二级汉字字库放在SPIRAM

文章目录 1.字库制作2.字库烧录到ESP32-S3的flash2.1 配置好分区文件2.2 汉字库文件烧录到ESP32的flash 3.将字库从 flash 拷贝到 SPIRAM3.1 工程配置中有关 SPIRAM 部分3.2 将汉字库从flash拷贝到SPIRAM的代码3.3 在进入lvgl之前调用函数 copyHZK_from_flash_to_SPIRAM 在前面…

Python入门知识点分享——(八)文件的open方法

学完了Python当中的数据类型&#xff0c;下一步我们来了解如何用Python语言打开文件并添加内容。 目录 file mode buffering encoding errors newline closefd opener 函数 打开文件需要用到open函数&#xff0c;完整的语法格式如下所示&#xff0c;为了演示方便&…

【Linux系统基础】(1)Linux基础命令全面详解

在计算机世界中&#xff0c;Linux操作系统以其稳定性、安全性和开源性而受到广大程序员和系统管理员的喜爱。然而&#xff0c;对于初学者来说&#xff0c;Linux的命令行界面可能会显得有些复杂和难以理解。本文将详细介绍一些常用的Linux基础命令&#xff0c;帮助你更好地理解和…

TLC2543(12位A/D转换器)实现将输入的模拟电压显示到数码管上

代码&#xff1a; #include <reg51.h> #define uchar unsigned char #define uint unsigned int// 数码管0-9 unsigned char seg[] {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; sbit SDO P1^0; sbit SDI P1^1; sbit CS P1^2; sbit CLK P1^3; s…

【C语言】打印内存数据

C语言&#xff0c;用函数封装&#xff1a;16进制打印unsigned char *p指向的内存&#xff0c;长度为int l。16个字节&#xff0c;换一次行。16个字节用一个字符串缓存&#xff0c;一次打印。 以下是一个使用函数封装的C语言代码&#xff0c;用于以16进制格式打印unsigned char …

MyBatis——MyBatis的延迟加载

MyBatis的延迟加载&#xff08;一对多查询案例&#xff09; 1.什么是延迟加载&#xff1f; 开启延迟加载后&#xff0c;在真正使用数据的时候才发起级联查询&#xff0c;不用的时候不查询。 2.pojo User类&#xff1a; package com.wt.pojo;import java.io.Serializable; …

计算机毕业设计------JSP教务处学生成绩管理系统

项目介绍 本项目包含管理员、教师、学生三种角色&#xff1b; 用户角色包含以下功能&#xff1a; 修改密码,查看自己的信息,查看自己的成绩,登录界面等功能。 管理员角色包含以下功能&#xff1a; 修改示例,增删改查学生信息,增删改查教师信息,增删改查课程信息,管理员修改…

电机控制 相关基础概念

基本概念: 定子或者转子上有铁心或者绕铜线的地方,绕铜线的地方叫槽,而将槽分开的叫齿,将所有的齿连起来的部位较轭部。 磁感应强度与磁场强度之间的关系可以通过以下公式表示: B=μH 其中,B 是磁感应强度,H 是磁场强度,μ 是磁导率。这个关系表明,在给定磁场强度下…

Golang 通用代码生成器仙童发布 2.4.0 电音仙女尝鲜版一及其介绍两个模式的视频

Golang 通用代码生成器仙童发布 2.4.0 电音仙女尝鲜版一及其介绍两个模式的视频 Golang 通用代码生成器仙童已发布 2.4.0 电音仙女尝鲜版一及其介绍视频。视频请见&#xff1a; 正常模式&#xff1a; https://www.bilibili.com/video/BV1fw411V77i/ 哑数据模式&#xff1a;…

@z-utils组 重构和自动化实现

highlight: monokai theme: github 包简介 z-utils组 是一个可以在vue/react/pure js 中使用的工具包&#xff0c;它包含三个子类&#xff0c;分别为 z-utils/base, z-utils/react, z-utils/vue 三个分别在不同区域使用。 他是原 zzy-javascript-devtools 的重构版本&#xf…

嵌入式系统复习--Thumb指令集

文章目录 上一篇Thumb指令集概述Thumb指令详细介绍数据处理指令数据存储指令转移指令异常中断指令 下一篇 上一篇 嵌入式系统复习–ARM指令集(二) Thumb指令集概述 在编写Thumb指令时&#xff0c;先要用伪指令CODE16声明&#xff08;ADS的编译环境下&#xff09;&#xff0c…

程序设计的思想

程序设计思想是指在程序设计过程中所采用的一种思维方式&#xff0c;它是程序设计的灵魂和基础。程序设计思想的正确与否直接关系到程序的质量和可维护性。在实际的程序设计中&#xff0c;我们需要遵循一定的程序设计思想&#xff0c;以确保程序的正确性、可读性和可维护性。 …

序列化和反序列化对比分析,序列化和反序列化输出十个学生信息截图

序列化和反序列化是数据处理中的两个相对的概念&#xff0c;通常用于对象的存储和传输。下面是对这两个过程的对比分析&#xff1a; 序列化&#xff08;Serialization&#xff09; 定义 目的&#xff1a; 将对象的状态信息转换成可以存储或传输的形式&#xff08;如XML, JSO…

循环渲染ForEach

目录 1、接口说明 2、键值生成规则 3、组件创建规则 3.1、首次渲染 3.2、非首次渲染 4、使用场景 4.1、数据源不变 4.2、数据源组项发生变化 4.3、数据源数组项子属性变化 5、反例 5.1、渲染结果非预期 5.2、渲染性能降低 Android开发中我们有ListView组件、GridVi…

linux:IP地址、修改主机名、域名解析、虚拟机配置固定IP

一:IP地址 1、每一台联网的电脑都会有一个地址&#xff0c;用于和其它计算机进行通讯 2、IP地址主要有2个版本,V4版本和V6版本(V6很少用暂不涉及) 3、IPv4版本的地址格式是:a.b.c.d,其中abcd表示0~255的数字,如192.168.88.101就是一个标准的IP地址 4、可以通过命令:ifconfi…

视频监控技术经历了哪些发展阶段?视频监控技术未来趋势展望

随着城市经济的发展和进步&#xff0c;视频监控也已经应用在人们衣食住行的方方面面&#xff0c;成为社会主体的一个重要组成部分。随着视频监控的重要性越来越凸显&#xff0c;大家对视频监控技术的发展也非常关注。今天我们来简单阐述一下&#xff0c;视频监控技术经历的几个…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)ChannelMap 模块的实现

&#xff08;三&#xff09;ChannelMap 模块的实现 这个模块其实就是为Channel来服务的&#xff0c;前面讲了Channel这个结构体里边它封装了文件描述符。假如说我们得到了某一个文件描述符&#xff0c;需要基于这个文件描述符进行它对应的事件处理&#xff0c;那怎么办呢&…

windos/ubuntu20.4下UE4.27.2像素流送

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…

企业出海-如何保护客户账户安全?

近年来国内企业竞争日益激烈&#xff0c;许多企业在这般环境下难以持续发展。那么该如何获得业务的可持续性增长&#xff0c;如何获取更多的客户的同时开阔公司的视野&#xff1f;出海便是如今帮助国内企业能快速发展壮大的潮流之一&#xff0c;摆脱了局限于国内发展的束缚奔向…