C++——类和对象(中)(2)尚未完结

news2024/9/21 2:43:13

拷贝构造

概念

在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。

那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

拷贝构造函数: 只有单个形参该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

特征

拷贝构造函数也是特殊的成员函数,其特征如下
1.拷贝构造函数是构造函数的一个重载形式
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错因为会引发无穷递归调用。

3.若未显式定义,编译器会生成默认的拷贝构造函数。
默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

实验日期类——浅拷贝

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, 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(2023, 10, 28);
	Date d2(d1);
	d1.Print();
	d2.Print();
}

在日期类中,我们不写拷贝构造函数时,使用编译器默认生成的可以完成值拷贝/浅拷贝,对于日期类足够使用。 

怎么验证呢?

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void F(Date d1)
{
	d1.Print();
}
int main()
{
	Date d1(2023, 10, 28);
	F(d1);
}

这里析构了两次,第一次析构是d1.print出了作用域要销毁去调用一次析构,第二次是析构是Date d1在程序结束之前自己去调用了一次析构(日期类不需要去析构销毁,这里只是为了下面拷贝构造的引用传参做铺垫)

为了减去第一次析构,使用引用做形参

加上引用以后d1.print出了作用域就不会销毁,自然不会去调用掉析构函数,因为Date&d1 就是d1的别名,它会在程序结束之前自己去调用析构(虽说是内存自己回收内置类型,这里调用Date的析构只是为了好理解拷贝构造的引用传参)。 

实验栈类——浅拷贝or深拷贝?

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

void f(Stack st1)
{
	//...
}
int main()
{
	Stack st;
	f(st);
}

为什么会出现程序崩溃?

因为 st1出了作用域会销毁,去调用析构函数,_a被释放了,后面st再去调用析构函数,导致_a再次被释放,共计了释放了两次(_a指向的空间被释放了两次)

那么怎么解决这个问题呢?

 规定:自定义类型对象拷贝的时候,必须调用一个函数叫拷贝构造。

初始化的时候就去调用拷贝构造

原因

栈类会造成_a释放两次 而日期类会造成无穷递归

调用拷贝构造 先传参 传值传参 形成新的拷贝构造 形成套娃

因此C++规定用引用传参

解决(正确使用拷贝构造)

拷贝构造(引用传值) 

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	Date(const Date& d)
	{
		_year == d._year;
		_month = d._month;
		_day = d._day;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

int main()
{

	Date d1(2023, 10, 28);
	Date d2(d1);

}

为了防止拷贝构造赋值传反,加上const修饰, &d(d1)的别名,隐含的this指针指向了d2,引用传值又减少了一次析构(虽然内置类型数据系统自己结束时回收)。

栈—拷贝构造(深拷贝)

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	深拷贝
	Stack(const Stack&stt)
	{
		cout << "Stack(const Stack&stt)" << endl;
		_a = (int*)malloc(sizeof(int) * stt._capacity);
		if(_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = stt._top;
		_capacity = stt._capacity;
		memcpy(_a, stt._a, sizeof(int) * stt._top);
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st;
	Stack st1(st);

}

Stack &stt是st的别名,this指针指向st1,最后把st的数据给st1转过去完成拷贝。

st先进去构造 然后st1进行深拷贝 出了作用域再去调用析构函数 最后程序结束前 st再去调用析构函数。

 

st和st1指向的空间都不同,所以不会出现同一个空间释放两次的问题。拷贝构造完美解决。 

 队列

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	深拷贝
	Stack(const Stack&stt)
	{
		cout << "Stack(const Stack&stt)" << endl;
		_a = (int*)malloc(sizeof(int) * stt._capacity);
		if(_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = stt._top;
		_capacity = stt._capacity;
		memcpy(_a, stt._a, sizeof(int) * stt._top);
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class Myqueque
{
	Stack _push;
	Stack _pop;
	int _sz = 0;
};

int main()
{
	Stack st;
	Stack st1(st);

	Myqueque q1;
	Myqueque q2(q1);

}

 队列又完成了拷贝构造,又是间接调用栈的深拷贝(拷贝构造),真是躺着赢下了游戏。

 栈写了拷贝构造——深拷贝 myqueque就能调用栈的拷贝构造。

总结

日期类不用写拷贝构造 使用编译器默认生成的就可以完成值拷贝——可以完成我们的需要

拷贝构造对内置类型完成默认的拷贝构造——浅拷贝

Date 和 MyQueue 默认生成拷贝就可以用

1、内置类型成员完成值拷贝

 2、自定义类型成员调用这个成员的拷贝构造stack需要自己写拷贝构造,完成深拷贝。

顺序表、链表、二又树等等的类,都需要深拷贝。

注意: 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用
尽量使用引用。

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

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

相关文章

模块化编程

1、函数单独写在一个文件中.c&#xff0c;然后声明也写在一个文件中.h,在mian.c中引用 2、安装目录下面的文件夹用<>,在自己文件夹目录下就是"" 3、创建自己的&#xff08;先把函数放在c文件&#xff0c;再创建头h文件&#xff09;

ruoyi-plus创建模块、自动生成代码

ruoyi-plus自动生成代码 1、创建模块 复制其他部分的resouce过来 修改yml文件 2 修改Nacos 2.1 修改数据库文件 复制其他数据库的链接 &#xff0c;改为自己新建的数据库名字 修改为自己要生成的数据库 新建数据库的yaml文件 3 重启docker的ruoyi-gen服务 docker re…

数据结构【DS】B树

m阶B树的核心特性: Q&#xff1a;根节点的子树数范围是多少&#xff1f;关键字数的范围是多少&#xff1f; A&#xff1a;根节点的子树数∈[2, m],关键字数∈[1, m-1]。 Q&#xff1a;其他结点的子树数范围是多少&#xff1f;关键字数范围是多少&#xff1f; Q&#xff1a;对任…

SSD1306 oled显示屏的驱动SPI接口

有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改&#xff0c;IIC接口官方有驱动&…

环形链表(C++解法)

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

Vmware下的虚拟机NAT连接后仍然木有网络

问题描述 出现在主机能ping通&#xff0c;互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么&#xff0c;NAT后可以访问互联网的前提是&#xff1a;这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中&#xff0c;进NAT配…

10000字!图解机器学习特征工程

文章目录 引言特征工程1.特征类型1.1 结构化 vs 非结构化数据1.2 定量 vs 定性数据 2.数据清洗2.1 数据对齐2.2 缺失值处理 原文链接&#xff1a;https://www.showmeai.tech/article-detail/208 作者&#xff1a;showmeAI 引言 上图为大家熟悉的机器学习建模流程图&#xff0c;…

TYWZOJ 礼品配对包装 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围与提示思路与部分实现完整代码 题目描述 爱与愁大神在这家目标店买了 2 x 2x 2x 份礼物&#xff0c;打算分给班级同学。其中有 x x x 份黑礼品&#xff0c; x x x 份白礼品&#xff0c; 2 x 2 2x2 2x2 个空…

计网小题题库整理第一轮(面向期末基础)(3)

基础选择题的最后一章更新&#xff0c;看完期末75至少没问题~ 前情提要&#xff1a; 计网小题题库整理第一轮&#xff08;12期&#xff09; 一.选择题 1、 目前,最流行的以太网组网的拓扑结构是&#xff08; C &#xff09;。 A&#xff09; 总线结构 B&#xff09; 环…

如何能够在发现问题和提问的时候一并带出自己的解决方案

1. 充分理解问题&#xff1a; 在提出问题之前&#xff0c;确保你已经完全理解了问题的本质。从不同的角度分析问题&#xff0c;确保没有遗漏任何重要的信息或者上下文。 2. 进行自我调查和研究&#xff1a; 在向他人寻求帮助之前&#xff0c;尝试自己解决问题。利用网络资源…

Go学习第十三章——Gin(入门与路由)

Go web框架——Gin&#xff08;入门与路由&#xff09; 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gon…

从零开始搭建Prometheus+grafana服务器组件监控系统

服务器及相关组件监控 本文档主要记录了常用企业级服务器及各种组件的监控手段和监控部署方案&#xff0c;使企业可以实时感知服务器组件的健康状态&#xff0c;并在服务器或组件出现异常时及时做出反应。 本方案采用的Prometheusgrafana的方式实现对服务器及各种组件的监控&am…

【前端】NodeJS核心知识点整理

1.Node.js入门案例 1.1.什么是Node.js JS是脚本语言&#xff0c;脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS&#xff0c;浏览器充当了解析器的角色。而对于需要独立运行的JS&#xff0c;NodeJS就是一个解析器。 每一种解析器都是一个运行环境&#xff0c;不但…

C/C++数据结构之深入了解线性表:顺序表、单链表、循环链表和双向链表

线性表是一种基本的数据结构&#xff0c;它在计算机科学中起着至关重要的作用。线性表用于存储一系列具有相同数据类型的元素&#xff0c;这些元素之间存在顺序关系。在C/C中&#xff0c;我们可以使用各种方式来实现线性表&#xff0c;其中包括顺序表、单链表、循环链表和双向链…

基于白鲸优化算法BWO优化的VMD-KELM光伏发电短期功率预测MATLAB代码(含详细算法介绍)

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; VMD适用于处理非线性和非平稳信号&#xff0c;例如振动信号、生物信号、地震信号、图像信号等。它在信号处理、振动分析、图像处理等领域有广泛的应用&#xff0c;特别是在提取信号中的隐含信息和去除噪声方面…

无头浏览器自动化:Puppeteer 帮你释放效能 | 开源日报 No.64

facebook/react Stars: 209.5k License: MIT React是一个用于构建用户界面的JavaScript库。它具有以下优势和特点&#xff1a; 声明式&#xff1a;React使得创建交互式UI变得轻松。您可以为应用程序中的每个状态设计简单视图&#xff0c;当数据发生更改时&#xff0c;React会…

Postman日常操作

一.Postman介绍 1.1第一个简单的demo 路特斯&#xff08;英国汽车品牌&#xff09;_百度百科 (baidu.com) 1.2 cookie 用postman测试需要登录权限的接口时&#xff0c;会被拦截&#xff0c;解决办法就是每次请求接口前&#xff0c;先执行登录&#xff0c;然后记住cookie或者to…

ChineseChess5 2023.10.28

中国象棋残局&#xff1a;黑双卒单车压境解棋

iphone备份后怎么转到新手机,iphone备份在哪里查看

iphone备份会备份哪些东西&#xff1f;iphone可根据需要备份设备数据、应用数据、苹果系统等。根据不同的备份数据&#xff0c;可备份的数据类型不同&#xff0c;有些工具可整机备份&#xff0c;有些工具可单项数据备份。本文会详细讲解苹果手机备份可以备份哪些东西。 一、ip…

婚礼的魅力

昨日有幸被邀请去当伴郎&#xff0c;虽然是替补&#xff0c;即别人鸽了&#xff0c;过去救急&#xff0c;但总归是去起作用。 婚礼的魅力&#xff0c;感受到了&#xff0c;满满的仪式感&#xff0c;紧凑的流程&#xff0c;还有不断的拍照&#xff0c;做视频&#xff0c;留下美好…