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

目录

拷贝构造函数

特性

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

赋值运算符重载


拷贝构造函数

基本概念:只有单个形参,该形参是对本类类型对象的引用(一般常用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~

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

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

相关文章

IDEA 配置文件乱码,项目编码设置

见下图 其中第一二项控制全局以及工程的编码格式&#xff0c;下方的则是 properties 配置文件的格式&#xff0c;统一调整为 UTF-8 后不再乱码

Java面试篇【并发编程】常见面试题(2024最新)

Java并发编程常见面试题 1.什么是线程和进程&#xff1f; 进程是操作系统分配资源的最小单位&#xff0c;各个进程之间占据独立的寻址空间&#xff0c;运行也是独立运行&#xff0c;进程间通信需要一些机制。进程间切换需要的开销较大。 线程是程序执行的基本单位&#xff0c…

如何在Linux系统Docker部署Dashy并远程访问内网服务界面

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Unity:Animation 三 Playable、ImportModel

目录​​​​​​​ 1. Playables API 1.1 Playable vs Animation 1.2 Advantages of using the Playables API 1.3 PlayableGraph Visualizer 2. Creating models outside of Unity 2.1 Preparing your model files for export 2.1.1 Scaling factors 2.1.2 优化模型文…

Unity中关于继承ScriptableObject的类

在游戏中我们会经常看到一些.asset的配置文件&#xff0c;而这些文件就是用一个自定义的类去继承ScriptableObject来生成的。比如当前有一些零散特效需要预加载&#xff0c;这个时候我们可以声明一个类去保存这些零散特效对象的信息&#xff0c;然后统一读取加载。 代码&#…

专访|云安全攻防:从理论到应用的全面探索

2023年11月&#xff0c;美国核研究实验室&#xff08;INL&#xff09;遭遇数据泄露。同年10月&#xff0c;索尼的员工数据在MOVEit攻击事件中被泄露。2024年2月&#xff0c;某知名制造商因云存储服务器的配置错误导致了敏感数据泄露。 这些事件表示企业必须重视云安全建设&…

【Memory协议栈】NVRAM Manager 模块介绍

目录​​​​​​​ 前言 正文 1.功能简介 2.关键概念 3.功能详解 3.1 内存硬件抽象层Ea/Fee的寻址方案 3.2 基本存储对象Basic storage objects 3.2.1 NV Block 3.2.2 RAM Block 3.2.3 ROM Block 3.2.4 Administrative block 3.2.5 NV Block Header 3.3块管理类型…

iostat命令详解

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 iostat是一个使用频率较高的命令&#xff0c;主要用来统计和输出CPU和磁盘IO信息。它的安装很简单&#xff1a; # yum -y insta…

20240307-1-前端开发校招面试问题整理JavaScript

前端开发校招面试问题整理【1】——JavaScript 1、JavaScript 基础 Q&#xff1a;介绍 js 的基本数据类型&#xff1f; 基本类型&#xff08;值类型&#xff09;&#xff1a;String&#xff0c;Number&#xff0c;Boolean&#xff0c;Null&#xff0c;Undefined&#xff0c;S…

创邻科技获评环紫金港创新生态圈智源创新企业

3月1日&#xff0c;由杭州城西科创大走廊管理委员会指导&#xff0c;中共杭州市西湖区委员会、西湖区人民政府主办的“环紫金港创新生态圈”行动推进大会暨2024年紫金港科技城经济高质量发展大会在杭州举办。凭借重要的生态位置和创新业务成果&#xff0c;创邻科技受邀参会并被…

安卓手机如何使用JuiceSSH实现公网远程连接本地Linux服务器

文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …

Java面试篇【MyCat】常见面试题(2024最新)

Mycat 1.Mycat 分库分表中间件&#xff0c;将存放在一个数据库的数据存放在不同的多个数据库中。来分散负载。 scheme 逻辑库&#xff0c;对应mysql的数据库&#xff0c;一个逻辑库定义了包含的所有table.是数据库集群对外的统一访问接口。table 逻辑表&#xff0c;和物理数…

08 |「Fragment 」

前言 实践是最好的学习方式&#xff0c;技术也如此。 文章目录 前言一、简介1、是什么2、为什么要有 Fragment3. Fragment 详细解释 二、Fragment 与 Activity 的直观理解三、Fragment 的创建1、Fragment 的创建方式2、Fragment 的增删替查1&#xff09; 替换&#xff08;常见&…

每日学习总结20240306

每日总结 20240306 1. 断言测试判断 #include <iostream> #include <assert.h> #include <cassert> #include <stdio.h>#define STR_OK "[\x1b[1;32m OK \x1b[0m]" #define STR_FAIL "[\x1b[1;31mFAIL\x1b[0m]"…

海外IP代理应用:亚马逊使用什么代理IP?

代理IP作为网络活动的有力工具&#xff0c;同时也是跨境电商的必备神器。亚马逊作为跨境电商的头部平台&#xff0c;吸引了大量的跨境电商玩家入驻&#xff0c;想要做好亚马逊&#xff0c;养号、测评都需要代理IP的帮助。那么应该使用什么代理IP呢&#xff1f;如何使用&#xf…

TikTok小店如何批量生成/上传产品视频?

有许多Shopee卖家都会遇到这样的问题&#xff1a;明明产品标题、描述优化了&#xff0c;产品主图也认真做了&#xff0c;但是自己的Shopee店铺还是没转化! 可能是忽略了产品视频。 在Shopee官方的交流沙龙中&#xff0c;Shopee官方讲师提及&#xff1a;“有视频的产品比没有视…

插件WebApiClient.JIT报异常Cannot access a disposed object

调第三方接口使用的是插件WebApiClient.JIT 这个插件很好用&#xff0c;一直使用的都没问题&#xff0c;但是今天却出现了一个奇怪的问题&#xff0c;放在循环里调接口抛异常“Cannot access a disposed object” 调了login接口后IsDisposed true&#xff0c;再使用_erpOutAp…

基于Llama 2家族的提示词工程:Llama 2 Chat, Code Llama, Llama Guard

Prompt Engineering with Llama 2 本文是学习 https://www.deeplearning.ai/short-courses/prompt-engineering-with-llama-2/ 的学习笔记。 文章目录 Prompt Engineering with Llama 2What you’ll learn in this course [1] Overview of Llama Models[2] Getting Started wi…

【DP】蓝桥杯第十三届-费用报销

#include<iostream> #include<algorithm> #include<cstring> #include<set> #include<queue> using namespace std; const int N1010; int dp[N][5010];//dp[i][j]:选到第i个物品是否能取到价值j&#xff1b; int month[13]{0,31,28,31,30,31,30…

Vivado HLS学习笔记

任意精度的数据类型 u 代表 unsigned&#xff0c;fixed代表定点数据&#xff0c;即常数 采用任意精度的数据类型可以使用更少的资源&#xff0c;硬件友好性 数据类型定义在 header file 中 表示任意进制 ap_int<6> a("101010",2); //二进制数据101010 ap_in…