当前位置: 首页 > 编程日记 > 正文

C++重新认知:拷贝构造函数

一、什么是拷贝构造函数

对于简单变量来说,可以轻松完成拷贝。

int a = 10;
int b = a;

但是对于复杂的类对象来说,不仅存在变量成员,也存在各种函数等。因此相同类型的类对象是通过拷贝构造函数来完成复制过程的。

#include<iostream>

using namespace std;

class String
{
private:
    char* m_buffer;
	unsigned int m_size;
public:
	String(const char* string)
	{
		m_size = strlen(string);
		m_buffer = new char[m_size + 1];
		memcpy(m_buffer, string, m_size);
		m_buffer[m_size] = 0;
	}
	~String()
	{
		delete[] m_buffer;
	}
	friend ostream& operator <<(ostream& stream, const String& string);
};
ostream& operator <<(ostream& stream,const String& string)
{
	stream << string.m_buffer;
	return stream;
}
int main()
{
	String a = "A";
	String b = a;//这里执行拷贝构造函数
	cout << a << endl;
	cout << b << endl;
	getchar();
	return 0;
}


在这里插入图片描述
可以看到成功拷贝了我们自己设计的String类(这里拷贝指的是默认拷贝构造函数即也是浅拷贝),当然这段代码是存在问题的
在这里插入图片描述
我们可以看到两块指向m_buffer内存地址相同,所以会出现多次析构的情况(即a对象析构完了,将m_buffer释放掉后,因为b对象指向的也是m_buffer区域又要释放一次已经释放掉的),这也是浅拷贝,下面会详细讲解浅拷贝和深拷贝。

二、拷贝构造函数的格式

拷贝构造函数的格式

类名(const 变量类型& other)
{
.......
}

要注意变量一定是本类型的可引用变量

三、拷贝构造函数的调用时机

1. 对象以值传递的方式传入函数参数

在这里插入图片描述

2.对象以值传递的方式从函数返回

在这里插入图片描述

3.对象需要通过另一个对象进行初始化

Test a(0,1);
Test b(a);
Test c = a;

四、浅拷贝和深拷贝

1.浅拷贝(上面样例中有涉及到这里就不在详细讲)

2.深拷贝

为了解决浅拷贝导致拷贝的对象指向内存与原被拷贝对象一致,导致析构两次的问题,我们需要手动提供另一种拷贝构造函数的方式————深拷贝。

#include<iostream>

using namespace std;

class String
{
private:
    char* m_buffer;
	unsigned int m_size;
public:
	String(const char* string)
	{
		m_size = strlen(string);
		m_buffer = new char[m_size + 1];
		memcpy(m_buffer, string, m_size + 1);
		m_buffer[m_size] = 0;
	}

	String(const String& other)
		:m_size(other.m_size)
	{
		m_buffer = new char[m_size + 1];
		memcpy(m_buffer, other.m_buffer, m_size + 1);
	}

	~String()
	{
		delete[] m_buffer;//防止内存泄露
	}
	char& operator[](unsigned int index)
	{
		return m_buffer[index];
	}
	friend ostream& operator <<(ostream& stream, const String& string);
};
ostream& operator <<(ostream& stream,const String& string)
{
	stream << string.m_buffer;
	return stream;
}
int main()
{
	String a = "A";
	String b = a;//这里执行拷贝构造函数
	b[0] = 'B';
	cout << a << endl;
	cout << b << endl;
	getchar();
	return 0;
}

深拷贝函数代码

String(const String& other)
		:m_size(other.m_size)
	{
		m_buffer = new char[m_size + 1];
		memcpy(m_buffer, other.m_buffer, m_size + 1);
	}

在这里插入图片描述
在这里插入图片描述
可以看到两个对象的m_buffer地址不再一样。即拷贝的对象又重新分配了一份自己的m_buffer地址。

但是当我们比如有一些函数,我们本来就不需要让他拷贝(例如输出之类的函数),因为拷贝他们没有任何意义。
这里举一个例子:
在这里插入图片描述
在这里插入图片描述
这里执行后的结果:

在这里插入图片描述
可以看到,跟我们上面二中对象以值传递的方式传入函数参数说的一样它会调用拷贝函数,但是这是非常没有效率的,因为我们仅仅只是希望它输出,而非拷贝一份在输出,这样是没有任何意义的。
我们该如何做呢?很简单
我们只需要传引用(&)就可以
在这里插入图片描述
在这里插入图片描述
这样就可以防止无效拷贝导致效率降低的问题了。

重要:总是通过const引用去传递对象来减少无效的拷贝来增加效率

以上就是拷贝构造函数的所有内容了如果读者觉得本文章对你有收益,还请关注,点赞!

相关文章:

你了解计算机网络的发展历史吗?

计算机网络是指将一群具有独立功能的计算机通过被互联起来的,在通信软件的支持下,实现的系统。计算机网络是计算机技术与通信技术紧密结合的产物,两者的迅速发展渗透形成了计算机网络技术。简而言之呢,计算机网络就是实现两台计算机相互沟通的介质。

C#实现Excel合并单元格数据导入数据集

C#实现Excel合并单元格数据导入数据集

鸿蒙开发-ArkTS基础,它与TS区别在那?

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。说明: 也就是前端开发过程中所有的js/ts语法大部分支持的,比如es6中的箭头函数-模板字符串-promise-async/await-数组对象方法- 注意: 根据对下一代的Next版本的内部沟通,下一版本的ArkTs对类型最了更一步的限制。

一文详解TensorFlow模型迁移及模型训练实操步骤

当前业界很多训练脚本是基于TensorFlow的Python API进行开发的,默认运行在CPU/GPU/TPU上,为了使这些脚本能够利用昇腾AI处理器的强大算力执行训练,需要对TensorFlow的训练脚本进行迁移。

Android Studio点击Run背后发生了什么

当点击Run的时候到底发生了什么呢

Docker的介绍及安装&基本操作命令

在讲到Docker之前,我们先了解虚拟机与容器之间的区别VM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;容器,在宿主机器、宿主机器操作系统上创建Docker引擎,在引擎的基础上再安装应用。特性容器虚拟机启动秒级分钟级硬盘使用一般为MB一般为GB性能接近原生弱于系统支持量单机支持上千个容器一般几十个。

即将消失的五种编程语言?

学习路径困难必然导致非常有限的活跃用户,而 Haskell 的上一个最新的稳定版本是在 2010 年发布,这对于促进它本身的发展无济于事。Perl 于 1987 年开始流行时,它被誉为是适合任何一个人的编程语言,曾经有一段时间,每个人都用Perl编程,但是后来发生了一些事情,开发者开始在不知道原因的情况下添加越来越大的功能,也许这增加了了问题的复杂性。甚至它的作者似乎已经含蓄地解释了Perl的一些问题,并选择停止从2000年开始的Perl 6开发,关键是,似乎现在也没人想要在用Perl。

JAVA中线程的run()和start()有什么区别?

run() 方法:start() 方法:区别总结:结语我的其他博客在Java中,线程的 和 是与多线程编程相关的两个方法,它们有着不同的作用和用法。 方法是 类的一个普通方法,用于定义线程的主体逻辑。当直接调用 方法时,该方法会在当前线程的上下文中执行,而不会创建新的线程。直接调用 方法,不会实现多线程的并发执行,而只是在当前线程中按照顺序执行 方法的内容。 方法: 方法是 类的一个

一文带你吃透JSP,增删改查实战案例详细解读

不得不说,JSP 现在已经是一门十分老旧的技术了,学习编程时,不仅要学习优秀的前言技术,还要对基础有一定的把握,所以学习 JSP 时,我们只做了解,不用刨根问底花费大量的时间,得不偿失。理解 JSP 及其原理学会使用 EL 表达式和 JSTL 标签理解 MVC 模式和三层架构(重点)学习 JSP 到什么程度呢?我们只需要能够使用 JSP 相关技术能够实现简单数据的增删改查即可。_jsp实现增删改查

教新手解决api-ms-win-crt-runtime-l1-1-0.dll丢失问题,简单有效修复dll文件问题

在使用电脑的过程中突然提示api-ms-win-crt-runtime-l1-1-0.dll错误的信息,那么出现在这样的问题有什么办法可以很好的解决api-ms-win-crt-runtime-l1-1-0.dll错误呢?今天就教大家解决api-ms-win-crt-runtime-l1-1-0.dll错误的几种办法,比较适合不懂电脑的萌新宝宝!如果你电脑电脑出现了这样的问题那么可以来试试吧!

vue+element ui实现图片上传并拖拽进行图片排序

vue+element ui实现图片上传并拖住进行图片排序

Linux安装MongoDB教程

将解压后的 mongodb-linux-x86_64-rhel70-4.2.23 中的所有文件全部移动到 /usr/local/mongodb 中 :注意/*是所有子文件。也可以不用设置环境变量进行启动,但是不设置环境变量启动的话要每次启动写很多启动参数,比较麻烦,所以做好配置环境变量。在 mongodb 下创建 data 和 logs 目录,以及日志文件mongodb.log。在 /usr/local 目录中创建 mongodb 文件夹。启动 MongoDB(-conf 使用配置文件方式启动)

websocket介绍并模拟股票数据推流

Websockt是一种网络通信协议,允许客户端和服务器双向通信。最大的特点就是允许服务器主动推送数据给客户端,比如股票数据在客户端实时更新,就能利用websocket。

IDEA中在Java项目中添加Web模块 与配置tomcat服务器

现有项目添加直接走第二步。

将 OpenCV 与 gdb 驱动的 IDE 结合使用

能力这个漂亮的打印机可以显示元素类型、标志和(可能被截断的)矩阵。众所周知,它可以在 Clion、VS Code 和 gdb 中工作。Clion 示例安装移入 .放在方便的地方,重命名并移动到您的个人文件夹中。将“source”行更改为指向您的路径。如果系统中安装的 python 3 版本与 gdb 中的版本不匹配,请使用完全相同的版本创建一个新的虚拟环境,相应地安装并更改 python3 的路径。用法调试器中以前缀为前缀的字段是为方便起见而添加的伪字段,其余字段保持原样。

Windows Copilot 更新及使用教程

GitHub Copilot 是一款基于机器学习的代码生成工具,可以帮助开发者快速生成高质量的代码。以下是 Windows Copilot 的更新及使用教程:

Linux之后台执行命令:nohup和&的使用

如果不将 nohup 命令的输出重定向,输出将附加到当前目录的 nohup.out 文件中。command>out.file是将command的输出重定向到out.file文件,即输出内容不打印到屏幕上,而是输出到out.file文件中。2>&1是将标准错误(2)重定向到标准输出(&1),标准输出(&1)再被重定向输入到out.file文件中。作业在后台运行的时候,可以把输出重定向到某个文件中,相当于一个日志文件,记录运行过程中的输出。将sh test.sh任务放到后台,但是依然可以使用标准输入,

Python实现PDF—>Excel的自动批量转换(附完整代码)

tkinter适用于简单的 GUI 应用,对于入门级开发者和小型项目而言是一个良好的选择。PyQt、PySide、Kivy 和 wxPython 适用于需要更丰富功能、更现代外观或跨平台移动应用的项目,但可能需要更多学习和配置。选择 GUI 库的最佳方法取决于项目的需求、开发者的经验水平以及对不同库的个人偏好。

Linux系统中Java new Date()的时间和系统时间不一致

出现问题:new Date(),的时间和当前时间不一样,发现差了8小时,看到8小时就应该明白了,时区的问题。

CentOS 7 设置 Jar包、MinIO、Nginx 开机自启动

根据需要,自己修改 Description 和 ExecStart 的内容即可(ExecStart后面的java命令需要全路径)ExecStart 服务运行执行的命令,放上面创建的脚本位置。[Install] 服务安装的相关设置,可设置为多用户。如果用 yum install 命令安装的,如果使用源码手动编译的则需要手动创建。文件,xxx 就是自定义的服务名称。After:设置在某个服务启动后启动。Description:服务的描述。可以使用这个命令来查看服务启动日志。里面的环境变量是必须的,将。

linux中&和&&,|和||及分号(;)的用法

在linux中,我们经常会用到&和&&,|和||及分号,但是好多人对其会混淆,不明白其中的意思,今天为大家讲解一下&和&&,|和||及分号(;)各自的说明和用法。

TypeScript基础知识:类型断言

语法,我们可以将一个值断言为特定类型或将联合类型的变量断言为其中一个类型。在实际开发中,合理使用类型断言可以提高代码的可读性和维护性。中,类型断言是一种强制将一个值视为特定类型的方式。它允许开发人员在编译时指定变量的类型,从而获得更好的类型检查和代码提示。类型断言是一种告诉编译器某个值的具体类型的方法。中的一项强大特性,它允许开发人员在编译时明确指定变量的类型,以获得更好的类型检查和代码提示。中的类型断言,并提供丰富的示例代码帮助读者更好地理解和应用这一特性。将一个联合类型的变量断言为其中一个类型。

Spring boot 3 集成rocketmq-spring-boot-starter解决版本不一致问题

根据上篇文章使用Docker安装RocketMQ并启动之后,有个隐患详情见下文。如何解决rocketmq 和spring boot 3.x集成问题

docker-宿主机与容器的命令执行方法

宿主机命令容器执行程序、容器命令宿主机执行程序的方法。

改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)

改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)2021.10.30 复现TPH-YOLOv52021.10.31 完成替换backbone为Ghostnet2021.11.02 完成替换backbone为Shufflenetv22021.11.05 完成替换backbone为Mobilenetv3Small2021.11.10 完成EagleEye对YOLOv5系列剪枝支持2021.11.14 完成MQBench对YOLOv5系列量

Nginx实战 | 高性能HTTP和反向代理神器Nginx前世今生,以及它的“繁花之境”

Nginx 的历史可以追溯到 1990 年代末期,当时互联网开始迅速发展,传统的 HTTP 服务器如 Apache 开始显得力不从心,无法满足日益增长的访问量和并发请求。Nginx 的设计理念是追求极高的性能和稳定性,同时还具有较低的内存消耗和资源占用,这使得它能够处理大量并发请求,非常适合于需要处理高负载的服务器环境。通过这些测试和优化方法,你可以了解 Nginx 的性能瓶颈,并采取相应的措施来提高其性能。Nginx 的测试和优化可以通过多种方式进行,包括负载测试、性能测试、配置优化和代码级优化。

《mybatis》--大数据量查询解决方案

之前写百万以及千万的导出数据的时候,对于将数据写道csv文件并压缩这里没有什么大问题了,但是出现了其他问题为:1、我们需要将数据从数据库中拿出来,并且在进行装配的时候出现了一些问题。2、对于整体内存安全来说,如果直接将数据从数据库中拿出来百万级别以上的数据对于内存是非常不友好的。当问题出现比较大的时候会直接触发GC,造成瘫痪。目前开发以及项目测试的是更多的使用mybatis来进行开发的,所以本文章讨论以及解决的的就是如何使用mybaits来解决流式查询并单条处理的问题。

怎么选择数据安全交换系统,能够防止内部员工泄露数据?

数据泄露可能给企业带来诸多风险:财产损失、身份盗窃、骚扰和诈骗、经济利益受损、客户信任度下降、法律风险和责任等,《2021年度数据泄漏态势分析报告》中显示,在数据泄露的主体中,内部人员导致的数据泄漏事件占比接近60%。飞驰云联文件安全交换系统,可以满足企业多场景下的文件交换需求,帮助企业终结多工具、 多系统并行使用的局面,减少因文件交换行为分散带来的数据管理不集中、难以管控的问题, 帮助企业内部构建统一、安全的企业数据流转通道。对于不能下载保存的数据,使用截屏、录屏的方式窃取并外泄数据;

2024年,前端必会的console骚操作

调试。程序员们努力地避免的东西,只为在代码中制造更多的错误。编写无错误的代码是即使是最好的程序员也会觉得难以实现的。这就是为什么你应该总是调试代码。而调试JavaScript代码的最好方法之一就是了不起的。除此之外,还有更好的方法。这也正是本文的重点。告诉你与控制台交互的更好方法。在复杂的IDE中输入控制台会出现各种自动补全。在Visual Studio Code中输入console时的自动补全选项。与使用普通的不同,这里有一些更好的选择。使用这些使调试过程变得更加容易和快速。