C++的类和对象(四):拷贝构造函数

news2024/10/25 4:27:01

目录

拷贝构造函数

特性

自定义类型的传值传参和传引用传参对比

赋值运算符重载


拷贝构造函数

基本概念:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在创建一个已存在对象一模一样的新对象时由编译器自动调用

调用格式:类名 新对象(同类对象名)

类中格式: 类名(const 类名& 变量名)

作用:创建一个已存在对象一模一样的新对象

特性

1、拷贝构造函数是构造函数的一个重载形式(重名构成函数重载)

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

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

    //正确写法:Date(Date& d)
    //最佳写法:Date(const Date& d)
	Date(Date d)//错误写法
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024,3,7);
	Date d2(d1);
	return 0;
}

自定义类型的传值传参和传引用传参对比

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

//传值传参
void func1(Date d)//d是d1的拷贝
{

}

//传引用传参
void func2(Date& rd)//rd是d1的别名
{

}

int main()
{
	Date d1(2024,3,7);
	func1(d1);
	func2(d1);
	return 0;
}

调试过程:20240307_111203-CSDN直播

        在调用func1函数前传参的过程中(d1对象的成员变量的数值会传递给d)会先进入拷贝构造函数中,执行完拷贝构造后才会调用func1函数:

在调用func2函数前传参的过程中不会进入拷贝构造函数,在传参结束后会直接调用func2函数:

这是因为C++规定,将一个对象作为参数传递给函数时(如果参数是按值传递而不是引用)会调用拷贝构造函数,如果我们不额外的写这两个函数来找寻找传值调用和传参调用的区别而是直接将拷贝构造函数写成Date(Date A):

就会造成无限递归,由于我们想要用d1对象中的成员变量的值去初始化d2对象的成员变量的值,在传值传参时d1传递给拷贝构造函数的是一个它值(所有成员变量的值)的临时拷贝,这个值也可以被视为一个新的对象e1,想要做的是初始化这个新的对象然后将初始化的结果带回去,带不带的回去还是一说(传值调用,形参是实参的拷贝,对形参的改变不会影响实参),当我们尝试初始化这个对象时又会调用拷贝构造函数,此时又会生成e1的拷贝,新对象f1......

结论:直接记住拷贝构造函数的格式即可,当你尝试使用传值传参时,编译器会报错

调用函数时先传参后调用  

#include <stdio.h>
int func(int a)
{
	return a;
}

int main()
{
	func(5);
	return 0;
}

3、拷贝构造函数记得引用前要加const,const可以很好的保护被引用的对象(d1对象的值不能被d2对象的值初始化

        否则出现原本是想借用拷贝构造函数初始化新对象的成员变量,但是赋值两端的内容写反了,新对象的成员变量(随机值)反而把拷贝构造函数的成员变量初始化成随机值了

d2(d1)看起来只是将对象d1作为实参传递了,但实际上还有一个包含d2对象地址的this指针 

 4、若为显示定义拷贝构造函数,编译器会生成默认的拷贝构造函数,默认拷贝构造函数会对内置类型成员变量会进行值拷贝(将成员变量的字节依照原有的顺序一个一个的拷贝)也叫浅拷贝

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
    
    //没有显式定义拷贝构造函数
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024,3,7);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

5、 拷贝构造函数也是构造函数

#include <iostream>
using namespace std;

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}

    //强制编译器生成默认构造函数:	Time() = default;

	Time(const Time& t)
	{
		cout << "Time(const Time& t)" << endl;
		_hour = t._hour;
		_minute = t._minute;
		_mecond = t._mecond;
	}
private:
	int _hour;
	int _minute;
	int _mecond;
};

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	//内置类型
	int _year;
	int _month;
	int _day;

	//自定义类型
	Time _t;
};

int main()
{
	Date d1(2024,3,7);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

由于我们在Time类中提供了拷贝构造函数所以编译器不会生成默认构造函数,对此我们可以使用default关键字强制编译器生成默认构造函数

6、类中没有涉及资源申请时,拷贝构造函数是否写都可以,一旦涉及资源申请,则拷贝构造函数一定要写否则就是浅拷贝

#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
	//默认构造函数
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
			
		_size = 0;
		_capacity = capacity;
	}

	//插入函数
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	//使用默认拷贝构造函数

	//析构函数
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};

int main()
{
	Stack s1;
    s1.Push(1);
    s1.Push(2);
    s1.Push(3);
    s1.Push(4);
	Stack s2(s1);
	return 0;
}

这是因为,实例化s1对象时在堆上开辟了个空间用于存放元素,接着s2对象要用s1对象的成员变量进行初始化,Stakc类没有显示定义拷贝构造函数,编译器会生成一份默认的拷贝构造函数,默认拷贝构造函数是按值拷贝的,即将s1中的内容原封不动的拷贝到s2中,对于_size和_capacity没问题,但是_array存放的可是s1在堆上开辟的空间的地址,此时将该地址的值也原封不动的传递给了s2的_array,此时s1和s2对象的_array指向同一片空间,当main函数结束时,s2先销毁,s2销毁时调用析构函数释放掉申请的空间,由于s1不知道,所以会将该空间再次释放,同一块内存空间的多次释放肯定会造成程序崩溃

因此对于申请了空间资源的类我们要进行深拷贝:

#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
	//默认构造函数
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
			
		_size = 0;
		_capacity = capacity;
	}

	//插入函数
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	//深拷贝
	Stack(const Stack& s)
	{
		DataType* tmp = (DataType*)malloc(s._capacity * (sizeof(DataType)));
		if(tmp == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(tmp, s._array, sizeof(DataType) * s._size);
		_array = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}

	//析构函数
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};

int main()
{
	Stack s1;
	Stack s2(s1);
	return 0;
}

~over~

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

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

相关文章

每日OJ题_牛客_井字棋

目录 牛客_井字棋 解析代码 牛客_井字棋 井字棋__牛客网 解析代码 class Board {public:bool checkWon(vector<vector<int> > board) {// 当前玩家是否胜出&#xff01;&#xff01;&#xff01;不是有玩家胜出int row board.size(), col board[0].size();fo…

基于similarities的文本语义相似度计算和文本匹配搜索

similarities 实现了多种相似度计算、匹配搜索算法&#xff0c;支持文本、图像&#xff0c;python3开发。 安装 pip3 install torch # conda install pytorch pip3 install -U similarities或 git clone https://github.com/shibing624/similarities.git cd similarities py…

基于R语言lavaan的SEM在复杂统计建模中的科研技术新突破

此外&#xff0c;我们还将深入探讨R语言的基础知识、结构方程模型的基本原理、lavaan程序包的使用方法等内容。无论是潜变量分析、复合变量分析&#xff0c;还是非线性/非正态/缺失数据处理、分类变量分析、分组数据处理等复杂问题&#xff0c;我们都将一一为您解析。 希望通过…

数据结构与算法-线性查找

引言 在计算机科学领域&#xff0c;数据结构和算法是构建高效软件系统的核心要素。今天我们将聚焦于最基础且广泛应用的一种查找算法——线性查找&#xff0c;并探讨其原理、实现步骤以及实际应用场景。 一、什么是线性查找&#xff1f; 线性查找&#xff08;Linear Search&am…

ChaosBlade故障注入工具--cpu,内存,磁盘占用\IO,网络注入等

前言&#xff1a; 本文介绍一款开源的故障注入工具chaosblade&#xff0c;该工具原本由阿里研发&#xff0c;现已开源&#xff1b;工具特点&#xff1a;功能强大&#xff0c;使用简单。 该工具故障注入包含&#xff1a;cpu&#xff0c;内存&#xff0c;磁盘io&#xff0c;磁盘…

2024.3.7 FreeRTOS 作业

思维导图 练习题 1.使用ADC采样光敏电阻数值&#xff0c;如何根据这个数值调节LED灯亮度。 //打开定时器3的通道3&#xff0c;并且设置为PWM功能HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1…

牛客网 华为机试 坐标移动

本题是需要将输入的字符串&#xff0c;得到移动位置的信息&#xff0c;同时要判断移动信息的合法性。 所以我们可以考虑先通过正则表达式过滤得到正确的字符串。 正确的字符串应该以ADWS其中一个字母开头&#xff0c;然后后面接着1个或者2个&#xff08;0-9&#xff09;的数字。…

灵根孕育源流出,心性修持大道生

解法&#xff1a; 手动本地跑了一下1e9&#xff0c;显然超时。 然后预处理发现开不了这么大的数组。 肯定有规律&#xff0c;打表看看 代码如下 #include<iostream> #include<vector> #include<algorithm> #include<cmath> using namespace std; #…

NetSuite Mass Update 批量更新功能

NetSuite中有一个小而精的便捷功能&#xff0c;但是也是一个很容易在实践中被大家遗忘的隐藏功能&#xff0c;就是Mass Update批量更新&#xff0c;在此想和各位分享一下&#xff5e;该功能主要是可以帮助用户快速将符合固定标准的记录中的单个/多个字段直接进行批量更新。如果…

016集——n等分cad多段线、弧、圆等——vba实现

cad命令行输入“div”选择图元后可n等分图元&#xff0c;若图中有大量图元需要n等分&#xff0c;这时可借助vba一键实现。 代码逻辑框架为&#xff1a;通过创建句柄函数来选择实体&#xff0c;通过sendcommand函数向命令行输入命令。 先来个小程序练练手&#xff1a;在屏幕上指…

leetcode 热题 100_滑动窗口最大值

题解一&#xff1a; 双端队列&#xff1a;滑动窗口的本质是在窗口末尾添加一个元素&#xff0c;并移除头部的一个元素。对于添加的元素&#xff0c;直接和当前最大值比较即可&#xff0c;但对于移除的元素&#xff0c;如果移除的是原先的最大值&#xff0c;则需要重新遍历窗口寻…

【语法】C++学习

注意 使用devc对vector初始化使用vector v1 {1, 2, 3, 4, 5}; 报错 解决 在编译器选项中加入以下代码 -static-libgcc -stdc11

MySQL-查询SQL语句的执行过程:连接器->查询缓存(8就没了)->分析器->优化器->执行器->返回结果

MySQL-查询SQL语句的执行过程&#xff1a;连接器->查询缓存<8就没了>->分析器->优化器->执行器->返回结果 查询SQL语句的执行过程1、主要步骤2、实用案例 查询SQL语句的执行过程 1、主要步骤 在MySQL中&#xff0c;一条查询SQL语句的执行过程非常复杂且…

利用auto-py-to-exe库的简单图形界面实现.py到.exe的转换

文章目录 1. auto-py-to-exe 简介2. 安装与使用3. 配置项介绍4. 打包完成 1. auto-py-to-exe 简介 运行 .py 文件需要配套相应的 Python 解释器和相关的依赖项&#xff0c;而很多时候我们会面临光有待演示的 .py 程序&#xff0c;而没有支持演示的环境的尴尬。一种解决办法就是…

代码随想录 回溯算法-子集

目录 78.子集 90.子集|| 491.非递减子序列 78.子集 78. 子集 中等 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的 子集 &#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#…

教程篇:Groq API+沉浸式翻译插件 体验最快AI翻译

1、进入https://console.groq.com/keys 申请一个API&#xff08;目前免费&#xff01;抓紧白嫖&#xff09; 2、安装Chrome插件&#xff1a;沉浸式翻译。 https://immersivetranslate.com/ 3、照着抄&#xff08;注意将apikey&#xff0c;换成自己申请的groq的api-key&…

算法刷题Day1 | 704.二分查找、27.移除元素

目录 0 引言1 二分查找1.1 我的解题1.2 修改后1.3 总结 2 移除元素2.1 暴力求解2.2 双指针法&#xff08;快慢指针&#xff09; &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;代码随想录算法训练营第一天…

什么是单点登录(SSO)前端用 iframe 实现单点登录 超详细说明!!

目录 什么是单点登录&#xff1f; 使用 iframe 实现单点登录 什么是单点登录&#xff1f; 单点登录的英文名叫做&#xff1a;Single Sign On&#xff08;简称SSO&#xff09;。 单点登录是一种身份验证过程&#xff0c;允许用户通过一次登录验证即可访问多个应用程序或服务…

达梦数据库基础操作(二):表空间操作

达梦数据库基础操作(二)&#xff1a;表空间操作 1. 表空间操作 1.1 达梦表空间介绍 表空间的概念&#xff1a; 每个DM 数据库都是由一个或者多个表空间组成&#xff0c;表空间是一个逻辑的存储容器&#xff0c;它位于逻辑结构的顶层&#xff0c;用于存储数据库中的所有数据&am…