【C++】拷贝复制:拷贝构造函数的使用

欢迎来到CILMY23的博客

本篇主题为:拷贝复制:拷贝构造函数的使用

博客主页:CILMY23-CSDN博客

个人专栏:Python | C++ | C语言 | 数据结构与算法

感谢观看,支持的可以给个一键三连,点赞关注+收藏。


写在前头:

构造函数函数名和类名相同,而析构函数是在前面加个 ~ ,我们也总结了最好是全缺省的构造函数更实用,以及构造函数和析构函数的调用顺序(链接),并且默认成员函数和默认构造函数也存在区别:

  • 默认构造函数不传参就可以调用的函数,例如:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都算默认构造函数,而且默认构造函数只能有一个
  • 默认成员函数:用户没有显式实现,编译器会生成的成员函数 

本篇我们将介绍默认的成员函数其中特殊的拷贝构造函数


目录

一、什么是拷贝构造函数?

二、如何正确使用拷贝构造函数

2.1 拷贝构造函数的特点

2.2 拷贝构造函数的应用场景

2.3、无穷递归调用的源头--传值传参和传引用传参

2.3.1 传值传参

2.3.2 传引用传参 

2.3.3 使用 const 修饰 


一、什么是拷贝构造函数?

拷贝构造函数(有时又称为复制构造函数)是一种特殊的构造函数在对象创建时用于从同一类的现有对象中初始化新对象

也就是在创建对象时,创建一个与已存在对象一某一样的新对象。它只有单个形参该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。拷贝构造函数可以通过深拷贝或浅拷贝来创建新对象的副本,具体取决于类的设计和需求。

例如:假设我们仍然有一个简易的学生类

class Student
{
public:
	Student(const char* name, int age, const char* ID)
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}
	void ShowInfo()
	{
		cout << "学生姓名:" << _name << endl;
		cout << "学生年龄:" << _age << endl;
		cout << "学生学号:" << _ID << endl;
	}
private:
	char _name[20];
	int _age;
	char _ID[20];
};

int main()
{
	Student stu1("xiaohong",20,"20020221");
	stu1.ShowInfo();

	Student stu2(stu1); // 拷贝构造函数
	stu2.ShowInfo();
	return 0;
}

二、如何正确使用拷贝构造函数

2.1 拷贝构造函数的特点

 拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用(看第三大点,传值传参和传引用传参)
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。  默认的拷贝构造函数对象按内存存储按 字节序完成拷贝这种拷贝叫做浅拷贝,或者值拷贝。(深浅拷贝区别可以看链接)注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
  4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗? 当然像日期类这样的类是没必要的。但是对有申请空间资源的,还是需要自己实现的。

2.2 拷贝构造函数的应用场景

拷贝构造函数典型调用场景:

  1. 使用已存在对象创建新对象
  2. 函数参数类型为类类型对象
  3. 函数返回值类型为类类型对象 

对第三点:我们使用日期类举例

//函数返回值为类类型对象
class Date
{
public:
	Date(int year, int minute, int day)
	{
		cout << "Date(int,int,int):" << endl;
	}

	Date(const Date& d)
	{
		cout << "Date(const Date& d):" << endl;
	}

	~Date()
	{
		cout << "~Date():" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

Date Test(Date d)
{
	Date temp(d); 
	return temp; //调用拷贝构造函数
}

int main()
{
	Date d1(2022, 1, 13); 
	Test(d1);

	return 0;
}

2.3、无穷递归调用的源头--传值传参和传引用传参

拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用

第二点说不用引用,会导致无穷递归调用,在了解之前我们得清楚C++中传值传参传引用传参的区别。

2.3.1 传值传参

在C语言中,传值传参会完成一个拷贝,而C++中自定义类型的拷贝会调用对应的拷贝构造函数,这点在C语言中是没有的。C语言中结构体传值传参,也可以传,但是会在某些情况下会出问题。

也就是我们看以下这段代码:

class Student
{
public:
	Student(const char* name, int age, const char* ID)//有参的构造函数
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}

	Student(Student stu) //拷贝构造函数
	{
		strcpy(_name, stu._name);
		strcpy(_ID, stu._ID);
		_age = stu._age;
	}

private:
		char _name[20];
		int _age;
		char _ID[20];
};

int main()
{
	Student stu1("xiaoming",21,"2020066S16");
	Student stu2(stu1);

	return 0;
}

它的过程是这样的:

我们想将 stu1 拷贝给 stu2 ,但是在这之前对自定义类型的值调用对应的拷贝构造函数,于是对 stu1 对象进行拷贝,对自定义类型的拷贝会继续调用拷贝构造函数,将 stu1 将拷贝给 stu ,但是传值 stu1 前,又将对参数 stu1 拷贝,最终将形成一个无穷递归调用。为了解决这个问题,我们就用上了引用传参

2.3.2 传引用传参 

如果我们使用传引用传参,则可以将这个问题解决,

代码:

class Student
{
public:
	Student(const char* name, int age, const char* ID)//有参的构造函数
	{
		strcpy(_name, name);
		strcpy(_ID, ID);
		_age = age;
	}

	Student(Student& stu) //拷贝构造函数
	{
		strcpy(_name, stu._name);
		strcpy(_ID, stu._ID);
		_age = stu._age;
	}

private:
	char _name[20];
	int _age;
	char _ID[20];
};

int main()
{
	Student stu1("xiaoming", 21, "2020066S16");
	Student stu2(stu1);

	return 0;
}

过程: 

因为是引用,所以我们根本不需要调用对应的拷贝构造函数,从而解决无穷递归调用这个问题。而且为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用就尽量使用引用

2.3.3 使用 const 修饰 

Student(const Student& stu) //拷贝构造函数
{
	strcpy(_name, stu._name);
	strcpy(_ID, stu._ID);
	_age = stu._age;
}

对拷贝的对象来说,我们并不希望改变对象的值,所以我们会在引用前加一个const,以用来保证安全性。 


总结:

  • 所有的自定义类型,最后还是内置类型,它们类似一个套娃的形式
  • 析构函数并不是所有类都需要
  • 对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝,通常是对内置类型拷贝
  • 内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
  • 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以。一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
  • 拷贝构造函数也是构造函数,如果只写了拷贝构造函数,在调用默认构造函数时可以用default关键字强制编译器生成。例如:Student() = default;
  • C++中自定义类型的拷贝会调用对应的拷贝构造函数,这点在C语言中是没有的。

感谢各位同伴的支持,本期拷贝构造函数篇就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【趣味实践】KataGo+Sabaki搭建Ai围棋助手

前言 最近和同门在比试围棋&#xff0c;结果被爆虐&#xff0c;于是想借助Ai治治“嚣张”的他。 KataGo简介 继2016年AlphaGo出圈以来&#xff0c;已有不少Ai模型&#xff0c;其中部分如下图[1]所示。 在线围棋对弈网站OGS上&#xff0c;使用KataGo(https://online-go.com/)这…

什么是限流?常见的限流算法

目录 1. 什么是限流 2. 常见限流算法 3. 固定窗口算法 4. 滑动窗口算法 5. 漏桶算法 6. 令牌桶算法 7. 限流算法选择 1. 什么是限流 限流&#xff08;Rate Limiting&#xff09;是一种应用程序或系统资源管理的策略&#xff0c;用于控制对某个服务、接口或功能的访问速…

硬件知识积累 DP 接口简单介绍以及 DP信号飞线到显示屏的问题

1. DP 接口的介绍 定义与起源&#xff1a; DP接口是由PC及芯片制造商联盟开发&#xff0c;并由视频电子标准协会&#xff08;VESA&#xff09;标准化的数字式视频接口标准。它的设计初衷是为了取代传统的VGA、DVI和FPD-Link&#xff08;LVDS&#xff09;接口&#xff0c;以满足…

QT-QTCreator环境配置

准备工作&#xff1a; 下载QT: 链接&#xff1a;https://pan.baidu.com/s/1prJcsC4DGqhKiXvLuPQFVA?pwd60b3 提取码&#xff1a;60b3下载WindowsKits&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1QNiS3HpbH5M5kXx5AhkqnQ?pwde2h8 提取码&#xff1a;e2h8安装的…

Java数组深度剖析:掌握数据结构的基石

引言 在编程世界中&#xff0c;数仅仅是一种数据类型&#xff0c;它是理解内存分配、多维数据处理以及性能优组像是构建复杂数据结构的基本积木。它们简洁、高效&#xff0c;是管理元素集的首选方式。在Java中&#xff0c;数组不化的关键。 这篇文章致力于深入探讨Java数组的各…

android studio项目实战——备忘录(附源码)

成果展示&#xff1a; 1.前期准备 &#xff08;1&#xff09;在配置文件中添加权限及启动页面顺序 ①展开工程&#xff0c;打开app下方的AndroidManifest.xml,添加权限&#xff0c;如下&#xff1a; <uses-permission android:name"android.permission.CAMERA"…

【二】电力系统规约IEC 104详解

电力系统规约IEC 104详解 概述 很早就准备梳理出一下电力系统规约系列的文章&#xff0c;因为自己在实践过程中发现这方面太难找了&#xff0c;网上的资料也都比较陈旧。我接触和使用IEC系列规约也有一段时间了&#xff0c;本着总结和分享的想法&#xff0c;我想推出这系列的文…

在线的调试器pythontutor,支持C/C++

1. 背景介绍 对于C语言的学习最复杂的可能无疑就是指针的&#xff0c;指针因其灵活、晦涩难懂等特点而出名&#xff0c;本文并不介绍利用gdb的角度去分析它&#xff0c;而是通过一个在线网站而分析&#xff1b; 2.C代码调试 3. C代码调试 4在线网站 https://pythontutor.com/…

【项目纪实】某国有航空公司人力资源系统诊断咨询项目

公司的人力资源管理问题一直都比较严重&#xff0c;比如人员冗余、员工工作积极性差等问题&#xff0c;虽然经过多次的管理尝试&#xff0c;存在的问题仍然没有缓解。华恒智信人力资源咨询公司的老师特别专业&#xff0c;帮我们系统、全面的诊断了人力资源管理上存在的问题&…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.3--Cortex-A7寄存器介绍

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Numerical Analysis(byRichard.L..Burden)【pdf高清英文原版】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

HarmonyOS 4.0(鸿蒙开发)01 - 怎么学习鸿蒙引导篇

作为公司的全栈开发工程师 以及 未来的发展是有鸿蒙这个阶段的&#xff0c;以及本身具有这个技术栈由此后续会分享自己在实战中学习到的东西&#xff0c;碰到的bug都会分享出来&#xff0c;这是引导篇期待后续的更新 学习目标&#xff1a; 理解HarmonyOS操作系统的架构和开发…

目标检测算法YOLOv3简介

YOLOv3由Joseph Redmon等人于2018年提出&#xff0c;论文名为&#xff1a;《YOLOv3: An Incremental Improvement》&#xff0c;论文见&#xff1a;https://arxiv.org/pdf/1804.02767.pdf &#xff0c;项目网页&#xff1a;https://pjreddie.com/darknet/yolo/ 。YOLOv3是对YOL…

解决IDEA下springboot项目打包没有主清单属性

1.问题出现在SpringBoot学习中 , 运行maven打包后无法运行 报错为spring_boot01_Demo-0.0.1-SNAPSHOT.jar中没有主清单属性 SpringBoot版本为 2.6.13 Java 版本用的8 解决方法 1.执行clean 删除之前的打包 2.进行打包规范设置 2.1 3.进行问题解决 (借鉴了阿里开发社区) 使用…

利用PDAL2.7.1 实现点云滤波

利用PDAL2.7.1 实现点云滤波 本文介绍利用PDAL实现点云滤波方法&#xff0c;包含pipeline命令行运行、C代码两种方法&#xff0c;C代码分别介绍对点云文件进行滤波、点云全部在内存中进行滤波的pdal两种调用方法。并简单探究pdal的设计结构。 目录 1 pipeline命令调用方法2 文…

R语言4版本安装mvstats(纯新手)

首先下载mvstats.R文件 下载mvstats.R文件点此链接&#xff1a;https://download.csdn.net/download/m0_62110645/89251535 第一种方法 找到mvstats.R的文件安装位置&#xff08;R语言的工作路径&#xff09; getwd() 将mvstats.R保存到工作路径 在R中输入命令 source(&qu…

飞腾D2000+X100 TYPE6全国产核心板

飞腾D2000X100 TYPE6核心板 产品概述 飞腾D2000X100 TYPE6核心板为增强型自主控制器核心板&#xff0c;其核心芯片CPU采用飞腾D2000/8核工业版CPU、飞腾桥片X100、双通道DDR4L插槽、PHY芯片等。 产品特点 l 基于飞腾D2000X100桥片 l 丰富的PCIE扩展资源&#xff0c;一路PCIE…

C++入门系列-函数重载

&#x1f308;个人主页&#xff1a; 羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 函数重载 自然语言当中&#xff0c;一个词可以有多重含义&#xff0c;人们可以通过上下文来判断该词真实的含义&#xff0c;即该词被重载了。 函数重载的概念 函数重载&#x…

A4的PDF按A3打印

先用办公软件打开&#xff0c;比如WPS。 选择打印-属性。 纸张选A3&#xff0c;如果是双面打印&#xff0c;选短边装订&#xff0c;然后在版面-页面排版-每张页数&#xff08;N合1&#xff09;选2。 不同打印机的具体配置可能不一样&#xff0c;但大体都是这个套路。

rocketmq dashboard控制台中topic状态无法展示

现象 在使用rocketmq控制台查看topic状态和订阅状态时&#xff0c;出现错误和没有信息的情况。 原因 rocketmq控制台版本问题&#xff0c;最新版本为1.0.1&#xff0c;支持rocketmq5版本&#xff0c;如果使用rocketmq4版本的服务无法兼容对应的数据。同理1.0.0版本也无法兼容ro…