C++ 浅拷贝和深拷贝

news2025/1/22 11:42:50

目录

1. 浅拷贝

2. 深拷贝


1. 浅拷贝

        浅拷贝只是拷贝一个指针,并没有新开辟一个地址,拷贝的指针和原来的指针指向同一块地址,如果原来的指针所指向的资源释放了,那么再释放浅拷贝的指针的资源就会出现错误

        对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。

举个栗子:

#include <iostream>  
using namespace std;
 
class Student
{
private:
	int num;
	char *name;
public:
	Student();
	~Student();
};
 
Student::Student()
{
	name = new char(20);
	cout << "Student" << endl;
 
}
Student::~Student()
{
	cout << "~Student " << (int)name << endl;
	delete name;
	name = NULL;
}
 
int main()
{
	{// 花括号让s1和s2变成局部对象,方便测试
		Student s1;
		Student s2(s1);// 复制对象
	}
	system("pause");
	return 0;
}

       执行结果:调用一次构造函数,调用两次析构函数,两个对象的指针成员所指内存相同,这会导致什么问题呢?name指针被分配一次内存,但是程序结束时该内存却被释放了两次,会导致崩溃!

       这是由于编译系统在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,进行的是浅拷贝!即对指针name拷贝后会出现两个指针指向同一个内存空间。

       所以,在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。

2. 深拷贝

        深拷贝不仅拷贝值,还开辟出一块新的空间用来存放新的值,即使原先的对象被析构掉,释放内存了也不会影响到深拷贝得到的值。在自己实现拷贝赋值的时候,如果有指针变量的话是需要自己实现深拷贝的。

举个栗子:

#include <iostream>  
using namespace std;
 
class Student
{
private:
	int num;
	char *name;
public:
	Student();
	~Student();
	Student(const Student &s);//拷贝构造函数,const防止对象被改变
};
 
Student::Student()
{
	name = new char(20);
	cout << "Student" << endl;
 
}
Student::~Student()
{
	cout << "~Student " << (int)name << endl;
	delete name;
	name = NULL;
}
Student::Student(const Student &s)
{
	name = new char(20);
	memcpy(name, s.name, strlen(s.name));
	cout << "copy Student" << endl;
}
 
int main()
{
	{// 花括号让s1和s2变成局部对象,方便测试
		Student s1;
		Student s2(s1);// 复制对象
	}
	system("pause");
	return 0;
}

        执行结果:调用一次构造函数,一次自定义拷贝构造函数,两次析构函数。两个对象的指针成员所指内存不同。

        总结:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

再说几句:
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:
1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;

2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。

3.浅拷贝带来问题的本质在于析构函数释放多次堆内存,使用std::shared_ptr,可以完美解决这个问题。

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

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

相关文章

在VSCode上画UML的三个插件

2023年9月2日&#xff0c;周六晚上 因为写代理模式的博客时需要画UML&#xff0c;所以就在网上找了半天&#xff0c; 最后觉得VSCode上的这三个插件比较好用 目录 三个画UML的VSCode插件PlantUMLDraw.io IntegrationUMLet我个人推荐使用PlantUML 三个画UML的VSCode插件 Pla…

实战-支付漏洞

免责声明 本文发布的工具和脚本&#xff0c;仅用作测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利&#xff0c…

A 股个股资金流 API 数据接口

A 股个股资金流 API 数据接口 全量股票资金流数据&#xff0c;全量A股数据&#xff0c;最长30日历史数据 1. 产品功能 支持所有A股资金流数据查询&#xff1b;每日定时更新数据&#xff1b;支持多达 30 日历史数据查询&#xff1b;超高的查询效率&#xff0c;数据秒级返回&am…

springboot 与 Redis整合

SpringBoot 操作数据&#xff1a;Spring-data jpa jdbc mongodb redis! SpringData 也是和SpringBoot 齐名的项目&#xff01; 说明&#xff1a;在SpringBoot2.X 之后&#xff0c;原来使用的jedis被替换成了lettuce jedis&#xff1a; 采用的直连&#xff0c;多个线程操作的话&…

【Unity3D】UI Toolkit简介

1 前言 UI Toolkit 是一种基于 Web 技术的 GUI 框架&#xff0c;是为了解决 UGUI 效率问题而设计的新一代 UI 系统&#xff08;UGUI 的介绍详见→UGUI概述&#xff09;。与 UGUI 不同&#xff0c;UI Toolkit 没有采用 GameObject 的方式&#xff0c;而是参考了 Web 技术的 XML …

深度学习应用-WeNet语音识别实战01

概括 本文对WeNet声音识别网络的Python API上介绍的Non-Streaming Usage和 Streaming-Usage分别做了测试&#xff0c;两者本质相同。API对应采样的声音帧率、声道都做了限制。效果还可以&#xff0c;但是部分吐字不清晰、有歧义的地方仍然不能识别清晰。 项目地址&#xff1a; …

内部类总结

内部类 1、内部类介绍&#xff1a; 外 2、成员内部类&#xff1a; 3、静态内部类 4、局部内部类&#xff1a; 5、匿名内部类&#xff1a;

说说TIME_WAIT和CLOSE_WAIT区别

分析&回答 TCP协议规定&#xff0c;对于已经建立的连接&#xff0c;网络双方要进行四次握手才能成功断开连接&#xff0c;如果缺少了其中某个步骤&#xff0c;将会使连接处于假死状态&#xff0c;连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接&#xf…

使用 Hue 玩转 Amazon EMR(SparkSQL, Phoenix) 和 Amazon Redshift

现状 Apache Hue 是一个基于 Web 的交互式 SQL 助手&#xff0c;通过它可以帮助大数据从业人员&#xff08;数仓工程师&#xff0c;数据分析师等&#xff09;与数据仓库进行 SQL 交互。在 Amazon EMR 集群启动时&#xff0c;通过勾选 Hue 进行安装。在 Hue 启用以后&#xff0…

Scratch 画画的技巧

前言 美术是一种艺术&#xff0c;且不局限于纸张&#xff0c;就像电脑绘图也属于美术。我至今已有三年多的画龄&#xff0c;经验丰富&#xff0c;尤其擅长在scratch造型编辑器上画矢量图。今天给大家分享一些实用的技巧。 1.讲解 用橡皮工具给一个圆擦出“橡皮洞” 橡皮工具&a…

2511. 最多可以摧毁的敌人城堡数目

文章目录 Tag题目来源题目解读解题思路复杂度分析写在最后 Tag 【数组】 题目来源 2511. 最多可以摧毁的敌人城堡数目 题目解读 在数组 forts 中&#xff0c;forts[i] 有三种数值&#xff1a; -1&#xff1a; 表示第 i 个位置没有城堡&#xff0c;是空地&#xff1b;0&…

【计算机知识】Base64 编码说明

一、理论 Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法&#xff0c;由于 2^664&#xff0c;所以每 6 个比特为一个单元&#xff0c;对应某个可打印字符。 Base64 常用于在通常处理文本数据的场合&#xff0c;表示、传输、存储一些二进制数据&#xff0c;包括…

程序员自由创业周记#2:前期准备

感恩 上次公开了创业的决定后&#xff0c;得到了很多亲朋好友和陌生朋友的鼓励或支持&#xff0c;以不同的形式&#xff0c;感动之情溢于言表。这些都会记在心里&#xff0c;大恩不言谢~ 创业方向 笔者是一名资质平平的iOS开发程序猿&#xff0c;创业项目也就是开发App卖&am…

图的深度优先与广度优先遍历

上篇博客介绍了图的概念与图的存储(邻接矩阵、邻接表)&#xff1a; 接下来就是介绍图的遍历。 图的遍历 给定一个图G和其中任意一个顶点v0&#xff0c;从v0出发&#xff0c;沿着途中各边访问图中的所有顶点&#xff0c;且每个顶点仅被遍历一次。"遍历"即对结点进行…

读SQL学习指南(第3版)笔记09_条件逻辑与事务

1. 条件逻辑 1.1. SQL逻辑根据特定列或表达式转向不同的分支来处理 1.2. 在程序执行时从多个路径中选取一个路径的能力 1.3. case表达式 1.3.1. 所有的主流数据库服务器都提供了旨在模拟大多数编程语言中if-then-else 语句的内建函数 1.3.1.1. Oracle的decode()函数 1.3.…

T113-S3-ov5640摄像头调试

目录 前言 一、ov5640模组介绍 1. 图像传感器特性 2. 接口和控制 3. 图像处理能力 4. 应用领域 二、原理图连接 三、设备树配置 四、驱动配置 五、ov5640使用 六、异常记录 总结 前言 摄像头模块是嵌入式系统中常见的外设&#xff0c;用于捕获图像和视频。在本篇文章…

一不留神就掉坑

乘除顺序问题 在据卡特兰数[1]公式,解决leetcode-96 不同的二叉搜索树[2]时,遇到一个非常诡异的问题, package mainimport "fmt"func main() { for i : 0; i < 40; i { fmt.Printf("第%d个卡特兰数为:%d\n", i, numTrees(i)) }}func numTrees(n int) i…

【超详细~KVM】KVM概述、安装及简单操作-------从小白到大神之路之学习运维第91天

第四阶段提升 时 间&#xff1a;2023年8月30日 参加人&#xff1a;全班人员 内 容&#xff1a; KVM概述、安装及简单操作 目录 一、KVM 概述 二、KVM工作原理 三、KVM应用场景 四、centos7 下安装部署 五、新建虚拟机步骤 1、创建存储池并创建存储卷 2、点击号创建…

MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区 由MPI自行维护的一块内存区域&#xff0c;也可由用户(MPI_Bsend)自行维护&#xff1b;发送方 维护一块发送缓冲区&#xff1b; 接收方 维护一块接收缓冲区。 数据收发过程&#xff1a; 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝&#xff0c;即数据到…

Redis——认识Redis

简单介绍 Redis诞生于2009年&#xff0c;全称是Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 特征 键值&#xff08;Key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&…