【C++ | 拷贝构造函数】一文了解C++的 拷贝(复制)构造函数

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-07 21:05:34

本文未经允许,不得转发!!!

目录

  • 🎄一、为什么需要 拷贝构造函数
  • 🎄二、什么是 拷贝构造函数
  • 🎄三、使用 拷贝构造函数
  • 🎄四、默认的 拷贝构造函数
  • 🎄五、总结



在这里插入图片描述

🎄一、为什么需要 拷贝构造函数

如果程序中出现需要 拷贝构造函数 的代码,而又没有提供拷贝构造函数时,系统会提供一个默认的拷贝构造函数,该函数只会完成浅拷贝而不会进行深拷贝,这就是为什么需要 拷贝构造函数 的原因。

关于浅拷贝、深拷贝的知识,可以看这篇文章:C++入门知识-拷贝构造函数-浅拷贝、深拷贝

下面用例子说明浅拷贝可能产生的问题:

// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 调用拷贝构造函数
	
	date_1.show();
	date_2.show();
	
	return 0;
}

上面代码由于没有进行深拷贝,导致 double free 了的错误,因为浅拷贝只复制了str的值,在两个对象销毁时都delete了。
在这里插入图片描述


在这里插入图片描述

🎄二、什么是 拷贝构造函数

拷贝构造函数,有些书也把其成为复制构造函数,其作用是将一个对象复制到新创建的对象中。

类的拷贝构造函数原型通常是这样的:类名(const 类名 &);。以CDate类为例,其拷贝构造函数如下:

CDate(const CDate &);

拷贝构造函数的几个特点:
1、函数名和类名相同,因为它也是构造函数的一种;
2、第一个参数必须是一个自身类类型的引用,且其他参数都有默认值。
3、第一个参数必须是自身类类型的引用的原因:如果不上引用则需要拷贝它的实参,为了要拷贝实参,又需要调用拷贝构造函数,如此无限循环;

清楚了这些之后,我们修改一下上个小节的代码,添加一个拷贝构造函数:

// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", m_year,m_mon,m_day);
	cout << "Calling Copy Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 调用date_2的拷贝构造函数
	
	date_1.show();
	date_2.show();
	
	return 0;
}

运行结果如下,添加拷贝构造函数后,运行不会报错,因为拷贝构造函数中重新new了内存:
在这里插入图片描述


在这里插入图片描述

🎄三、使用 拷贝构造函数

为了使用 拷贝构造函数,我们必须清楚 拷贝构造函数 在什么情况下会被调用,然后再根据自己设计的类是否需要深拷贝来决定怎样定义该类的 拷贝构造函数

会调用拷贝构造函数的几种情况:

  1. 使用同类型的对象去初始化另一个对象时。
    如下代码,使用了 date_1 初始化 date_2:
    CDate date_1(2024,06,05);
    CDate date_2 = date_1;	// 调用date_2的拷贝构造函数
    
  2. 将一个对象作为实参传递给一个非引用类型的形参。
    void printDate(CDate date)
    {
    	date.show();
    }
    ...
    printDate(date_2);	// 实参传值到形参,调用拷贝构造函数
    
  3. 从一个返冋类型为非引用类型的函数返回一个对象。
    CDate g_date(2024,06,06);;
    CDate getDate()
    {
    	return g_date;	// 3、返回对象时,调用拷贝构造函数
    }
    

下面例子演示了调用拷贝构造函数的这三种场景:

// g++ 11_Copy_Constructor_Date.cpp
#include <iostream>
#include <stdio.h>

using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	~CDate();							// 析构函数声明

	void show()
	{
		//cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
		cout << "Date: " << str << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
	char *str;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", year,mon,day);
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	str = new char[64];
	sprintf(str, "%4d.%02d.%02d", m_year,m_mon,m_day);
	cout << "Calling Copy Constructor" << ", this=" << this <<endl;
}

// 析构函数定义
CDate::~CDate()
{
	cout << "Calling Destructor" << ", this=" << this <<endl;
	delete [] str;
}

void printDate(CDate date)
{
	date.show();
}

CDate g_date(2024,06,06);;
CDate getDate()
{
	return g_date;	// 3、返回对象时,调用拷贝构造函数
}

int main()
{
	CDate date_1(2024,06,05);
	CDate date_2 = date_1;	// 1、调用date_2的拷贝构造函数
	
	date_1.show();
	date_2.show();
	cout << endl;
	
	printDate(date_2);	// 2、实参传值到形参,调用拷贝构造函数
	cout << endl;
	
	getDate();
	cout << endl;
	
	return 0;
}

运行结果:
在这里插入图片描述


在这里插入图片描述

🎄四、默认的 拷贝构造函数

如果没有为一个类定义拷贝构造函数,则编译器会合成一个“默认拷贝构造函数”。

默认的拷贝构造函数会逐个复制非静态成员( 成员复制也称为浅复制)的值到正在创建的对象中。根据成员类型有下面几种情况:
1、如果成员是内置类型,则直接复制;
2、如果成员本身就是类对象,则将使用这个类的拷贝构造函数来复制类对象;
3、如果成员是数组,默认的拷贝构造函数会逐元素地拷贝一个数组类型的成员。


在这里插入图片描述

🎄五、总结

👉本文介绍C++的拷贝构造函数,为什么需要拷贝构造函数,什么是拷贝构造函数,怎么使用拷贝构造函数,默认拷贝构造函数。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

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

相关文章

逆波兰表达式

目录 一、定义 二、算法步骤 三、代码实现 一、定义 逆波兰表达式又叫做后缀表达式&#xff0c;是一种没有括号&#xff0c;并严格遵循“从左到右”运算的后缀式表达方法。 二、算法步骤 1、首先构造一个运算符栈&#xff0c;此运算符在栈内遵循越往栈顶优先级越高的原则。 …

数据挖掘实战-基于Catboost算法的艾滋病数据可视化与建模分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

WindowServer2022配置iSCSI磁盘CHAP认证

实验环境: WindowsServer2022 一、实验环境 1、实验拓扑 为存储服务器添加4块100G的磁盘新建1个100G的iSCSI虚拟磁盘&#xff0c;发起目标选择IQN为保证连接安全&#xff0c;采用单向CHAP认证新建1个200G的iSCSI虚拟磁盘&#xff0c;发起目标选择IP地址为保证连接安全&#x…

DeepDriving | 多目标跟踪算法之SORT

本文来源公众号“DeepDriving”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;多目标跟踪算法之SORT 1 简介 SORT是2016年发表的一篇文章《Simple Online and Realtime Tracking》中提出的一个经典的多目标跟踪算法&#xff0c;…

【数据结构】栈和队列-->理解和实现(赋源码)

Toc 欢迎光临我的Blog&#xff0c;喜欢就点歌关注吧♥ 前面介绍了顺序表、单链表、双向循环链表&#xff0c;基本上已经结束了链表的讲解&#xff0c;今天谈一下栈、队列。可以简单的说是前面学习的一特殊化实现&#xff0c;但是总体是相似的。 前言 栈是一种特殊的线性表&…

flask_sqlalchemy时间缓存导致datetime.now()时间不变问题

问题是这样的&#xff0c;项目在本地没什么问题&#xff0c;但是部署到服务器过一阵子发现&#xff0c;这个时间会在某一刻定死不变。 重启uwsgi后&#xff0c;发现第一条数据更新到了目前最新时间&#xff0c;过了一会儿再次发送也变了时间&#xff0c;但是再过几分钟再发就会…

【全开源】JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码

&#xff1a;构建便捷出行新体验 一、引言&#xff1a;探索打车系统小程序源码的重要性 在数字化快速发展的今天&#xff0c;打车系统小程序已成为我们日常生活中不可或缺的一部分。它以其便捷、高效的特点&#xff0c;极大地改变了我们的出行方式。而背后的关键&#xff0c;…

啵啵啵啵啵啵啵啵啵啵啵啵啵啵啵

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

swaggerHole:针对swaggerHub的公共API安全扫描工具

关于swaggerHole swaggerHole是一款针对swaggerHub的API安全扫描工具&#xff0c;该工具基于纯Python 3开发&#xff0c;可以帮助广大研究人员检索swaggerHub上公共API的相关敏感信息&#xff0c;整个任务过程均以自动化形式实现&#xff0c;且具备多线程特性和管道模式。 工具…

增加强制索引依然慢

版本: 阿里云RDS MySQL 8.0.25 线上数据库CPU达到100%, 定位到如下SQL EXPLAIN SELECT ssd.goods_no,ssd.goods_name,ssd.goods_spec,ssd.goods_unit,ssd.create_time,w.warehouse_name,sb.batch_no,swl.warehouse_region_location_name,sc.customer_name AS goodsOwnerName,s…

如何在MySQL中实现upsert:如果不存在则插入?

目录 1 使用 REPLACE 2 使用 INSERT ... ON DUPLICATE KEY UPDATE 使用 INSERT IGNORE 有效会导致 MySQL 在尝试执行语句时忽略执行错误 INSERT 。这意味着 包含 索引或 字段 INSERT IGNORE 中重复值的语句 不会 产生错误&#xff0c;而只是完全忽略该特定 命令。其明显目的是…

2048小游戏的菜鸡实现方法

# 2048小游戏的实现与分析 2048是一款非常受欢迎的数字滑块游戏&#xff0c;其目标是通过滑动和合并相同数字的方块来创建一个值为2048的方块。下面&#xff0c;我们将通过分析一个C语言实现的2048小游戏的源代码&#xff0c;来探索如何用编程实现这款游戏。 ## 游戏概述 20…

指针(初阶1)

一.指针是什么 通俗的讲&#xff0c;指针就是地址&#xff0c;其存在的意义就像宾馆房间的序号一样是为了更好的管理空间。 如下图&#xff1a; 如上图所示&#xff0c;指针就是指向内存中的一块空间&#xff0c;也就相当于地址 二.一个指针的大小是多少 之前我们学习过&#x…

Springboot注意点

1.Usermapper里加param注解 2.RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody 是Spring框架中用于处理HTTP请求的两个不同的注 get请求一般用url传参数&#xff0c;所以参数名和参数的值就在ur…

LCM — Least Common Multiple 最小公倍数

因为任何一个数都可以表示为若干个质数幂的乘积。 比如75 3*5*5&#xff0c;即 2^0 * 3^1 * 5^2 * 7^0 ... 那么对于两个数来说&#xff0c;gcd就是他们取每个质数的较小幂的乘积&#xff0c;lcm则相反。显然&#xff0c;这些幂加起来就是他们乘积。 gcd(a,b) * lcm(a,b) a…

立创·天空星开发板-GD32F407VE-USART

本文以 立创天空星开发板-GD32F407VET6-青春版 作为学习的板子&#xff0c;记录学习笔记。 立创天空星开发板-GD32F407VE-USART 基础通信概念同步通信 & 异步通信串行通信 & 并行通信双工 & 单工通讯速率码元 串口通信数据帧 串口封装 基础通信概念 通信协议是网络…

本地运行ChatTTS

TTS 是将文字转为语音的模型&#xff0c;最近很火的开源 TTS 项目&#xff0c;本地可以运行&#xff0c;运行环境 M2 Max&#xff0c;差不多每秒钟 4&#xff5e;&#xff5e;5 个字。本文将介绍如何在本地运行 ChatTTS。 下载源码 首先下载源代码 git clone https://github…

WPF中读取Excel文件的内容

演示效果 实现方案 1.首先导入需要的Dll(这部分可能需要你自己搜一下) Epplus.dll Excel.dll ICSharpCode.SharpZipLib.dll 2.在你的解决方案的的依赖项->添加引用->浏览->选择1中的这几个Dll点击确定。(添加依赖) 3.然后看代码内容 附上源码 using Excel; usi…

TypeScript环境安装与VScode编辑器的使用

说明大背景环境&#xff0c;我用的是window10系统。 1.安装node.js 。 去官网下载安装包。 虽然我去的是官网&#xff0c;但是不知为何下载了个不知名的东西&#xff0c;后来又找了个链接才下载正确了。 实际上就是一个.msi的文件。我用的版本&#xff1a;node-v18.19.0-x6…

【第四节】C/C++数据结构之树与二叉树

目录 一、基本概念与术语 二、树的ADT 三、二叉树的定义和术语 四、平衡二叉树 4.1 解释 4.2 相关经典操作 4.3 代码展示 一、基本概念与术语 树(Tree)是由一个或多个结点组成的有限集合T。其中: 1 有一个特定的结点&#xff0c;称为该树的根(root)结点&#xff1b; 2 …