一、深拷贝示例




1、浅拷贝问题


在上一篇博客 【C++】深拷贝和浅拷贝 ③ ( 浅拷贝内存分析 ) 中 , 使用了浅拷贝 , 将 原始对象 Students 赋值给了 拷贝对象 Student s2 ;

使用 C++ 编译器 生成的 默认的拷贝构造函数 进行对象赋值 , 该拷贝是 浅拷贝 ;


使用浅拷贝被出现了两个问题 :

  • 浅拷贝 导致 两个对象持有相同的指针 , 修改 拷贝对象 指针指向的数据 , 原始对象 指针指向的数据也会一起修改 ;
  • 析构时 , 两个对象的指针都需要释放 , 释放第二个指针时 , 该指针已经被释放 , 再重复释放一个已经被释放的指针 , 直接报错 ;

在这里插入图片描述


2、自己实现深拷贝


上述 浅拷贝 中 , 只拷贝指针变量 , 没有重新为新对象的指针成员 变量分配内存 , 导致后续的一系列问题 ;

如果 自己要实现深拷贝操作 , 那么需要 在 拷贝构造函数中 , 一旦遇到指针成员变量 , 立刻测量该指针分配的堆内存大小 , 然后再新的内存中保存要拷贝的数据 ;


针对要拷贝的 Student 类中的 m_name 指针类型成员变量 , 深拷贝流程如下 :

  • 首先 , 获取 char* 类型指针 指向的 字符串长度 , 使用 strlen 函数测量指针指向的堆内存的大小 ;
// 获取字符串长度
int len = strlen(s.m_name);
  • 然后 , 为 对象的成员变量 m_name 指针分配内存 , 注意这是为 char* 类型字符串分配内存 , 还要为字符串结尾的 ‘\0’ 字符分配内存 ;
// 为 m_name 成员分配内存 
// 注意还要为字符串结尾的 '\0' 字符分配内存
m_name = (char*)malloc(len + 1);
  • 最后 , 使用 strcpy 函数 , 拷贝字符串内容 , 在 C++ 中需要添加 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义 , 否则会报错 ;
// 拷贝字符串
// C++ 中使用该函数需要
// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
if (m_name != NULL)
{
	strcpy(m_name, s.m_name);
}

自定义 深拷贝 拷贝构造函数代码示例 :

	// 拷贝构造函数
	// 执行 Student s2 = s; 代码时调用该函数
	// 自己实现 深拷贝 操作
	Student(const Student& s)
	{
		// 获取字符串长度
		int len = strlen(s.m_name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
			strcpy(m_name, s.m_name);
		}

		// 为 m_age 成员设置初始值
		m_age = s.m_age;

		cout << "调用拷贝构造函数" << endl;
	}




二、深拷贝完整代码示例



下面的代码中 , 自定义了 深拷贝 的拷贝构造函数 ;

执行 Student s2 = s; 代码时 , 自动调用了 自定义的 深拷贝 拷贝构造函数 , 拷贝时为指针成员重新再堆内存中分配了内存空间 , 并复制了字符串数据 ;

执行如下代码 , 单独修改拷贝对象 , 不会影响到原始对象 ;

// 修改 s2 对象
strcpy(s2.m_name, "Jey");

在最后析构时 , 由于 拷贝对象 和 原始对象 的 char* m_name; 指针成员变量分别指向不同的内存空间 , 两个对象析构 , 都不会影响另外一个对象的指针成员析构 ;


完整代码示例 :

#define _CRT_SECURE_NO_WARNINGS

#include "iostream"
using namespace std;

class Student
{
public:

	// 有参构造函数
	Student(int age, const char* name)
	{
		// 获取字符串长度
		int len = strlen(name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
			strcpy(m_name, name);
		}
			
		// 为 m_age 成员设置初始值
		m_age = age;

		cout << "调用有参构造函数" << endl;
	}

	// 拷贝构造函数
	// 执行 Student s2 = s; 代码时调用该函数
	// 自己实现 深拷贝 操作
	Student(const Student& s)
	{
		// 获取字符串长度
		int len = strlen(s.m_name);

		// 为 m_name 成员分配内存 
		// 注意还要为字符串结尾的 '\0' 字符分配内存
		m_name = (char*)malloc(len + 1);

		// 拷贝字符串
		// C++ 中使用该函数需要
		// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
		if (m_name != NULL)
		{
			strcpy(m_name, s.m_name);
		}

		// 为 m_age 成员设置初始值
		m_age = s.m_age;

		cout << "调用拷贝构造函数" << endl;
	}

	~Student()
	{
		// 销毁 name 指向的堆内存空间
		if (m_name != NULL)
		{
			free(m_name);
			m_name = NULL;
		}
		cout << "调用析构函数" << endl;
	}

	// 该类没有定义拷贝构造函数 , C++ 编译器会自动生成默认的拷贝构造函数

	// 打印类成员变量
	void toString()
	{
		cout << "m_age = " << m_age << " , m_name = " << m_name << endl;
	}

public:
	int m_age;
	char* m_name;
};

int main()
{
	// 调用有参构造函数 , 创建 Student 实例对象
	Student s(18, "Tom");
	// 打印 Student 实例对象成员变量值
	s.toString();

	// 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
	// 该操作会调用 默认的拷贝构造函数 
	// C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
	Student s2 = s;
	s2.toString();

	// 修改 s2 对象
	strcpy(s2.m_name, "Jey");
	s.toString();
	s2.toString();

	// 执行时没有问题 , 两个对象都可以正常访问
	// 但是由于拷贝时 执行的是浅拷贝 
	// 浅拷贝 字符串指针时 , 直接将指针进行拷贝 , 没有拷贝具体的值
	// s 和 s2 的 m_name 成员是同一个指针
	// 如果析构时 , 先析构 s2 , 将指针释放了 
	// 之后再析构 s 时 发现 继续释放 被释放的指针 , 报错了



	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
}

执行结果 :

调用有参构造函数
m_age = 18 , m_name = Tom
调用拷贝构造函数
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Jey
请按任意键继续. . .
调用析构函数
调用析构函数

Y:\002_WorkSpace\002_VS\HelloWorld\HelloWorld\Debug\HelloWorld.exe (进程 7480)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

在这里插入图片描述

相关文章

CentOS本地部署SQL Server数据库无公网ip环境实现远程访问

GeoServer是OGC Web服务器规范的J2EE实现,利用GeoServer可以方便地发布地图数据,允许用户对要素数据进行更新、删除、插入操作,通过GeoServer可以比较容易地在用户之间迅速共享空间地理信息。另外,GeoServer是开源软件。下面介绍GeoServer web ui 管理界面 结合cpolar 内网穿透工具实现远程访问,

各版本 操作系统 对 .NET Framework 与 .NET Core 支持

有两种类型的受支持版本:长期支持 (LTS) 版本和标准期限支持 (STS) 版本。所有版本的质量都是一样的。唯一的区别是支持的时间长短。LTS 版本可获得为期三年的免费支持和补丁。STS 版本可获得 18 个月的免费支持和修补程序。有关详细信息,请参阅。从上图中我们可以看出,.Net5及以下版本已经不再受到官方支持;而.Net7看起来也是过渡版本,支持时间较短,本文从 .Net Core 3.1 开始介绍支持的系统,可能不是很全面,仅供参考。

ElasticSearch 集群搭建与状态监控cerebro

在单机上利用docker容器运行多个es实例来模拟es集群。部署es集群可以直接使用docker-compose来完成,但要求Linux虚拟机至少有4GI的内存空间。&quot;number_of_replicas&quot;: 1 // 副本数。&quot;number_of_shards&quot;: 3,// 分片款量。kibana可以监控es集群,不过新版本需要依赖es的x-pack 功能,配置比较复杂。第一种方式:利用kibana的DevTools创建索引库 ,在DevTools中输入指令。第二种方式:利用cerebro创建索引库。

如何在 ChatGPT 上使用 Wolfram 插件回答数学问题

集成了 Wolfram 插件的 ChatGPT 能够向 Wolfram|Alpha 提出具体问题,并利用 Wolfram 的计算知识和数据资源生成更精确和准确的答案。这两者结合的方式是通过在 ChatGPT 上集成 Wolfram 插件,使 ChatGPT 能够利用 Wolfram|Alpha 的计算知识和 Wolfram 语言的强大功能。最关键的是,它不仅提供了解决方案的分步说明,还提供了答案的视觉参考,使得 Wolfram 成为在使用 ChatGPT 学习和解决数学问题时的理想辅助工具。

网络知识-以太网技术的发展及网络设备

大家都被互联网上各种各样的内容、技术闪亮了眼睛,没有太多人去了解比较底层的一些网络技术。面试的时候,我也问过很多技术人员,对以太网是否了解,了解多少?但是很多人都知之甚少!但是,在我们实际工作碰到问题、分析问题、定位问题、解决问题的时候,又必须要了解这方面的知识。以太网最初到现在的主要设备包括集线器、中继器、网桥、交换机。以太网目前应用在很多行业,在视频监控、安防、视频会议等领域都有很广泛的应用。

如何使用可视化管理工具DockerUI远程管理docker容器

DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基于容器安装方式,部署方便高效,浏览和维护docker单节点或集群节点worker和manager。DockerUI具有易于使用的界面。它不需要记住 docker 指令。只需下载镜像即可立即加入并完成部署。使用DockerUI并结合cpolar内网穿透可以更加轻松的管理docker和swarm,实现后台公网访问并管理,视觉性更加直观,后台开发更加便利。

在 Docker 中配置 MySQL 数据库并初始化 Project 项目

这样,您就完成了在 Docker 中配置 MySQL 数据库并初始化 Project 项目的过程。希望这篇博客对您有所帮助!创建目录 /project/mysql 以及 /project/mysql_data。在每个 SQL 文件中,将 AUTO_INCREMENT 修改为 1。将准备好的 SQL 文件复制到 /project/mysql 目录。将 init.sql 放到 /project/mysql 目录。在 SQL 文件中插入管理员相关数据。在 SQL 文件中插入机型相关数据。1.4. 插入管理员。

如何使用Plex在Windows系统搭建个人媒体站点公网可访问

用手机或者平板电脑看视频,已经算是生活中稀松平常的场景了,特别是各种碎片时间(追剧下饭、地铁上刷剧等等),看个喜欢的视频必不可少。但不知道为什么,各大影音平台总能轮流占住热播剧,还限定很多剧只能会员观看,搞得我们总有交不完的会员费。此时,拥有一个私人影音媒体站点就显得很有必要。今天,笔者就为大家介绍,如何使用cpolar+Plex组合,在Windows系统上搭建一个全能的私人媒体影音站点。

如何使用Node.js快速创建本地HTTP服务器并实现公网访问服务端

Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation(原为 Node.js Foundation,已与 JS Foundation 合并)持有和维护,亦为 Linux 基金会的项目。Node.js 采用 Google 开发的 V8 运行代码,使用事件驱动、非阻塞和异步输入输出模型等技术来提高性能,可优化应用程序的传输量和规模。这些技术通常用于资料密集的即时应用程序。Node.js 大部分基本模块都用 JavaScri

为什么ChatGPT选择了SSE,而不是WebSocket?

WebSocket是一种网络通信协议,它最早被提出来是为了解决HTTP连接的一大限制:HTTP协议中,一个客户端发送给服务端的请求必须由服务端返回一个响应,这使得服务端无法主动向客户端推送数据。客户端通过发送一个特殊的HTTP请求向服务器请求建立WebSocket连接。这个请求类似于:GET /chat HTTP/1.1 Upgrade: websocket Connection: Upgrade服务器响应这个请求,确认建立WebSocket连接。

网络安全-真实ip获取&amp;伪造与隐藏&amp;挖掘

proxy protocol没有研究,和TOA差不多,按照协议发包就行了,实现就交给读者吧。TOA的伪造方式还是不错的,非linux下没有btftools,可以自己写一个代理,把浏览器的流量转发到本地代理,代理的功能就是把TOA改一下。一些代理隐藏ip还是不错的,除非网站从开始没有使用cdn、部分使用cdn,或网站服务器有其他服务导致真实ip发出包了。该博客作者我也问了,一开始就使用了CDN,也没有其他子域名、服务,应该是无法找到真实IP了。

如何使用Docker将.Net6项目部署到Linux服务器(三)

尤其在测试环境,多个项目公用一个nginx的情况很多,这个时候,如果多个项目的nginx配置都放在一个conf配置文件中,会特别混乱。按照nginx配置,它应该监听4012端口,然后找到根目录/usr/local/publish_2023/forum_manage_vue,访问我们的vue网站,可是却提醒我们,该网页无法显示。# 注意,还可以写域名,但是需要额外的配置,后面示例会介绍,这个实例是简单的测试服务器的nginx配置、不过多说明。在nginx下的conf中,找到配置文件nginx.conf。

一篇文章深入认识微服务SpringCloud和Dubbo的区别

Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案。SpringCloud是目前国内使用最广泛的微服务框架。SpringCloud基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封之外,还有一些选型中立的开源组件。

Hadoop之MapReduce 详细教程

为了开发我们的 MapReduce 程序,一共可以分为以上八个步骤,其中每个步骤都是一个 class 类,我们通过 job 对象将我们的程序组装成一个任务提交即可。为了简化我们的 MapReduce 程序的开发,每一个步骤的 class 类,都有一个既定的父类,让我们直接继承即可,因此可以大大简化我们的 MapReduce 程序的开发难度,也可以让我们快速的实现功能开发。MapReduce 编程当中,其中最重要的两个步骤就是我们的 Mapper 类和 Reducer类Mapper 抽象类的基本介绍。

Jupyter Notbook+cpolar内网穿透实现公共互联网访问使用数据分析工作

在数据分析工作中,使用最多的无疑就是各种函数、图表、代码和说明文档,这些复杂的内容不仅让使用的人头晕脑胀,也让普通的聊天工具一脸蒙圈。沟通工具不给力,就没法协同办公,可数据分析又离不开多人配合,所以Jupyter Notebook就成为大部分数据工作人员的必备工具。正如之前所说,Jupyter Notebook很适应复杂内容的沟通,因此现在也在机器学习、深度学习和教育工作中获得广泛应用。但Jupyter Notebook也有缺陷,就是被局限在局域网范围。

React Query 实战教程:在 React 中如何优雅的管理接口数据状态?

如何通过Ajax或者Fetch优雅的请求后端接口,这是所有复杂前端项目都需要考虑处理的事情。在React项目中,有不少成熟的Hook能够让开发者管理整个请求过程中的数据和状态,例如use-httpswr甚至ahook中提供的useRequest。我曾经很长一段时间是直接使用ahook中的useRequest,但是有的项目中不需要ahook中的其他hook,我又不愿意仅仅为了使用useRequest而在项目中引入ahook。就像我第参与的一个前端项目中仅仅为了使用jquery的$.ajax而引入。

【C#】.net core 6.0 依赖注入生命周期

对于.net core而言,依赖注入生命周期有三种瞬态(Transient)、作用域(Scoped)和单例(Singleton),无论使用哪种生命周期,都需要确保对象的线程安全性,并正确地处理依赖关系。

如何搭建Tomcat服务并结合内网穿透实现公网访问本地站点

Tomcat作为一个轻量级的服务器,不仅名字很有趣(让人想起童年),也拥有强大功能,由于其可以实现JavaWeb程序的装载,就成为配置JSP和Java系统必备的环境软件,也是开发调试JSP程序的首选。Tomcat运行稳定且开源免费,加上apache和Sun的加持即免费和开源的特性,使其广泛应用在中小型系统及并发访问用户较少的场景中。但想要让Tomcat网页能在公共互联网环境下被访问到,就需要cpolar内网穿透的协助。现在。笔者就为大家介绍,如何使用cpolar内网穿透。

SpringMVC之获取请求参数和域对象共享数据

一、SpringMVC获取请求参数1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数6、通过POJO获取请求参数7、解决获取请求参数的乱码问题二、域对象共享数据1、使用ServletAPI向request域对象共享数据2、使用ModelAndView向request域对象共享数据3、使用Model向request域对象共享数据4、使用map向request域对象共享数据5、使用ModelMap向request域对象共享数据。

如何使用Docker部署Dashy并无公网ip远程访问管理界面

Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具结合cpolar内网工具,我们实现无需部署到公网服务器,即可实现公网访问Dashy,下面我们介绍配置方法。

C++归并排序详解以及代码实现

归并排序(Merge Sort)是一种采用分治法(Divide and Conquer)策略的排序算法。该算法首先将已有序的子序列合并,得到完全有序的序列。在归并排序中,合并操作是将两个有序表合并成一个有序表的过程。

如何在Linux设置JumpServer实现无公网ip远程访问管理界面

JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpServer 帮助企业以更安全的方式管控和登录所有类型的资产,实现事前授权、事中监察、事后审计,满足等保合规要求。下面介绍如何简单设置即可使本地jump server 结合cpolar 内网穿透实现远程访问jump server 管理界面.

复杂 SQL 实现分组分情况分页查询

在处理数据库查询时,分页是一个常见的需求。尤其是在处理大量数据时,一次性返回所有结果可能会导致性能问题。因此,我们需要使用分页查询来限制返回的结果数量。同时,根据特定的条件筛选数据也是非常常见的需求。在本博客中,我们将探讨如何根据 camp_status 字段分为 6 种情况进行分页查询,并根据 camp_type 字段区分活动类型,返回不同的字段。我们将使用 SQL 变量来实现这一功能,并通过示例进行详细解释。

docker搭建maven私库Nexus3

阿里代理地址:http://maven.aliyun.com/nexus/content/groups/public/由于nexus的默认端口为8081,我们在启动的时候改为18091后需要修改nexus的配置文件。这样就可以在本地浏览器进入nexus页面了,地址为 服务器ip:18091。右上角登录用户名为admin,密码为之前查看的密码。配置maven-central的代理地址。删除nuget开头的仓库。同时查看admin密码。
返回
顶部