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

news2024/12/23 19:46:31

欢迎来到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语言中是没有的。

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

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

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

相关文章

RCE学习(一)

一.知识点 RCE&#xff1a;远程命令/代码执行漏洞&#xff0c;简称为RCE漏洞&#xff0c;可以直接向服务器后台远程注入操作系统的命令或者代码&#xff0c;从而拿到服务器后台的权限。RCE分为远程执行命令&#xff08;执行ping命令&#xff09;和远程代码执行eval 简单来说就…

【数据结构】时间复杂度和空间复杂度解析

数据结构前言&#xff1a; 1. 什么是数据结构 打个比方来说不同的数据就相当于不同的书籍&#xff0c;我们经常在图书馆可以看到不同类别的书籍会被整理放在书架上方便查看存放&#xff0c;数据结构就是一种计算机存储管理数据的方式。 2. 什么是算法 算法就是一系列的计算…

蓝桥杯练习系统(算法训练)ALGO-953 混合积

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 众所周知&#xff0c;人人都在学习线性代数&#xff0c;既然都学过&#xff0c;那么解决本题应该很方便。   宇宙大战中&…

上位机图像处理和嵌入式模块部署(树莓派4b和pyqt5界面开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在大部分linux程序开发中&#xff0c;一般是没有界面的。不过不排除有些场合&#xff0c;是需要用界面进行数据交互、调参使用的。这种情况下一般就…

JavaScript+B/S版云LIS系统源码ASP.NET CORE 3.1 MVC云LIS系统如何实现样本追踪的预警功能?医院云LIS检验系统源码

JavaScriptB/S版云LIS系统源码ASP.NET CORE 3.1 MVC云LIS系统如何实现样本追踪的预警功能&#xff1f;医院云LIS检验系统源码 实验室信息管理系统&#xff08;Trasen Laboratory Information Management System&#xff09;是一套专业的医疗实验室信息管理软件&#xff0c;包含…

远程链接linux

远程连接 ssh 远程登录操作&#xff0c;ssh会对用用户进行身份信息的验证&#xff0c;会对两台主机之间发通信数据进行加密 安装 ssh 远程登录的服务端 yum install -y openssh-server启动 ssh 服务 systemctl start ssh.service 关闭 ssh 服务 systemctl stop ssh.service …

伦敦金的交易时间段都适合投资吗?

是所有的交易时间段都适合投资。首先&#xff0c;让我们了解伦敦金的交易时间。伦敦金市场的交易时间分为两个主要时段&#xff1a;亚洲盘和欧美盘。亚洲盘通常在北京时间早晨6点至下午5点半左右&#xff0c;而欧美盘则从北京时间晚上8点半开始&#xff0c;一直到次日早晨4点半…

tomcat篇-windows 运行tomcat的startup.bat时,终端打印的中文显示为乱码

当运行Tomcat的startup.bat时&#xff0c;如果终端中中文显示为乱码&#xff0c;这通常是因为Tomcat使用的日志输出编码与Windows命令行默认的编码不匹配。针对这一问题&#xff0c;你可以尝试以下步骤来解决&#xff1a; 1、执行startup.bat&#xff0c;在输出的窗口右击&…

HCIP第二节

OSPF&#xff1a;开放式最短路径协议&#xff08;属于IGP-内部网关路由协议&#xff09; 一。OSPF的数据包类型 3层报头 协议号89 1.Hello&#xff1a;周期收发&#xff0c;用于邻居发现&#xff0c;关系建立&#xff0c;周期保活-10s/30s&#xff08;路由之间相互认识&#…

如何搭建本地的 NPM 私有仓库 Nexus

NPM 本地私有仓库&#xff0c;是在本地搭建NPM私有仓库&#xff0c;对公司级别的组件库进行管理。在日常开发中&#xff0c;经常会遇到抽象公共组件的场景&#xff0c;在项目内部进行公用。新的项目开始时&#xff0c;也会拷贝一份创建一个新的项目&#xff0c;这样做不易于管理…

Day23.一刷数据结构算法(C语言版) 39组合总和;40组合总和II;131分割回文串

一、39组合总和 本题是集合里元素可以用无数次&#xff0c;那么和组合问题的差别&#xff0c;其实仅在于对startIndex上的控制 题目链接&#xff1a;组合总和 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法-组合总和 &#xff08;39.组合总和&#xff…

react核心知识

1. 对 React 的理解、特性 React 是靠数据驱动视图改变的一种框架&#xff0c;它的核心驱动方法就是用其提供的 setState 方法设置 state 中的数据从而驱动存放在内存中的虚拟 DOM 树的更新 更新方法就是通过 React 的 Diff 算法比较旧虚拟 DOM 树和新虚拟 DOM 树之间的 Chan…

界面组件DevExpress Blazor UI v23.2 - 网格、工具栏功能全新升级

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 DevExpress Blazor控件目前已经升级…

创新指南|如何通过用户研究打造更好的人工智能产品

每个人都对人工智能感到兴奋&#xff0c;但对错过机会 (FOMO) 的恐惧正在驱使公司将人工智能嵌入到每个产品功能中。这可能会导致以技术为中心的方法&#xff0c;从而掩盖产品开发的基本目标&#xff1a;创建真正解决用户问题并满足他们需求的解决方案。本文将介绍通过用户研究…

找不到msvcr100.dll怎么办,轻松解决msvcr100.dll丢失的5种方法

在我们日常与电脑相伴的工作与学习过程中&#xff0c;偶尔会遇到一些让人措手不及的软件运行问题。其中之一就是“msvcr100.dll丢失”。这个错误通常会导致某些程序无法正常运行。为了解决这个问题&#xff0c;本文将介绍5种常见的解决方法&#xff0c;帮助大家快速恢复程序的正…

Intelij Idea Push失败,出现git Authentication failed(验证失败)

目录 1、出现问题的原因 2、解决之法 1、出现问题的原因 能出现这种问题&#xff0c;最主要的原因是链接对上了&#xff0c;但用户验证失败了&#xff0c;即登录失败。 因为服务器转移或者换了git项目链接&#xff0c;导致你忘记了用户名密码&#xff0c;随意输入之后&…

Golang | Leetcode Golang题解之第58题最后一个单词的长度

题目&#xff1a; 题解&#xff1a; func lengthOfLastWord(s string) (ans int) {index : len(s) - 1for s[index] {index--}for index > 0 && s[index] ! {ansindex--}return }

【Docker】docker部署lnmp和搭建wordpress网站

环境准备 docker&#xff1a;192.168.67.30 虚拟机&#xff1a;4核4G systemctl stop firewalld systemctl disable firewalld setenforce 0 安装docker #安装依赖包 yum -y install yum-utils device-mapper-persistent-data lvm2 #设置阿里云镜像 yum-config-manager --add…

Recruit App

招聘类APP小程序

调教AI给我写了一个KD树的算法

我不擅长C&#xff0c;但是目前需要用C写一个KD树的算法。首先我有一份点云数据&#xff0c;需要找给定坐标范围0.1mm内的所有点。 于是我开始问AI&#xff0c;他一开始给的答案&#xff0c;完全是错误的&#xff0c;但是我一步步给出反馈&#xff0c;告诉他的问题&#xff0c;…