【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

news2024/11/13 23:06:43

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++初阶之路⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述


类和对象

  • 1. 前言
  • 2. 拷贝构造函数
    • 2.1 对拷贝构造函数参数的思考
    • 2.2 默认拷贝构造函数
    • 2.3 对拷贝构造函数的总结
  • 3. 运算符重载
    • 3.1 对运算符重载的思考
    • 3.2 特殊的赋值运算符
    • 3.3 前置++和后置++
    • 3.4 运算符重载再理解
  • 4. 总结以及拓展

1. 前言

本章重点:

本篇文章将详细讲解拷贝构造函数
和运算符重载,并介绍const成员的概念

拷贝构造函数和运算符重载
是类和对象中六大默认成员函数
的其中两个

拷贝构造类似于构造一个一模一样的你

在这里插入图片描述


2. 拷贝构造函数

我们在写代码的时候会遇见这种场景:

class Date
{
public:
	 Date(int year = 1900, int month = 1, int day = 1)
	 {
	 _year = year;
	 _month = month;
	 _day = day;
	 }
private:
	 int _year;
	 int _month;
	 int _day;
};
int main()
{
	 Date d1(2023,7,30);
	 Date d2(d1);//用d1初始化d2
	 return 0;
}

使用一个已经存在的对象初始化
一个正在定义的对象就是在拷贝构造!

拷贝构造函数特征:

  • 拷贝构造是构造函数的一个重载形式
  • 拷贝构造函数只能有一个参数且必须
    是类类型对象的引用!

比如Date类的拷贝构造:

 Date(const Date& d)
 {
	 _year = d._year;
	 _month = d._month;
	 _day = d._day;
 }

2.1 对拷贝构造函数参数的思考

为什么拷贝构造函数的参数
一定要是:类类型对象的引用?

因为拷贝构造是用另一个对象初始化
所以是类类型对象可以理解
但是为啥必须传引用?

这是因为不传引用会引起无限递归!

可以这样理解无限递归:

在这里插入图片描述
假如加上了引用,调用拷贝构造时
传对象的别名进函数,就不会有
形参拷贝一份实参的过程,就不会死递归!

所以拷贝构造函数一定要加上引用!


2.2 默认拷贝构造函数

如果用户不显示写拷贝构造函数
编译器会自动生成一个默认构造函数

默认构造函数会对内置类型完成:
值拷贝(浅拷贝)

默认构造函数会去调用自定义
类型的构造函数完成拷贝

对于内置类型来说,浅拷贝可能会有问题!

举个例子:

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 int& data)//插入函数
 {
	 _array[_size] = data;
	 _size++;
 }
 
 ~Stack()//析构函数
 {
	 if (_array)
	 {
		 free(_array);
		 _array = nullptr;
		 _capacity = 0;
		 _size = 0;
	 }
 }

private:
	 int *_array;
	 size_t _size;
	 size_t _capacity;
};

int main()
{
	Stack s1(10);
	s1.push(1);
	s1.push(2);
	s3.push(3);
	s3.push(4);
	s3.push(5);
	Stack s2(s1);
	retrn 0;
}

stack类没有显示写拷贝构造函数
编译器会自动生成默认拷贝构造函数

默认构造函数是按照字节方式直接拷贝的

所以对象s1和s2的关系图如下:

在这里插入图片描述

array指向的空间是动态开辟的
是在堆区存储的,使用值拷贝的方式
s1和s2中的array指向的空间相同!

这么做会有啥问题?

当s1和s2生命周期结束时
会分别调用它们各自的析构函数
然而两个对象中的指针指向的空间相同
析构函数会调用两个free释放空间!
同一份空间释放两个就会出错!


2.3 对拷贝构造函数的总结

  • 拷贝构造函数的参数必须是引用

  • 若类中没有涉及到空间申请
    则默认拷贝构造就够用了

  • 下面这两种写法都是在拷贝构造:

Date d1(2023,7,30);
Date d2(d1);
Date d3 = d1;

拷贝构造的典型应用场景:

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

3. 运算符重载

在日期类中定义一个对象:

Date d1(2023,7,31);

假设现在想要计算100天
后是几月几号的话,直接使用+
无法实现这个功能!

Date d2 = d1+100; //普通的加号不能实现此功能

C++中,普通的加号只适用于内置类型
而自定义类型的加号种类太多
系统没有自行实现,需要用户自己写!

要自己实现的运算符需要运算符重载!

C++为了增强代码的可读性
引入了运算符重载
运算符重载是具有特殊函数名的函数
也具有其返回值类型,函数名字
以及参数列表
其返回值类型和参数列表与普通函数类似

使用关键字: operator

函数原型:
返回值类型 operator操作符(参数列表)

举例说明日期类+号的重载:

Date operator+(Date d1, int x);

在这里插入图片描述


3.1 对运算符重载的思考

运算符重载是针对自定义类型的
所以函数参数中必须有一个类类型参数

但是,如果运算符重载写在类外
它就不能访问类中的私有成员!

所以常常将运算符重载写在类中!

比如:

class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
        _year = year;
        _month = month;
        _day = day;
 }
 bool operator==(const Date& d2)
 {
	  return _year == d2._year;
	      && _month == d2._month
	      && _day == d2._day;
 }
private:
 int _year;
 int _month;
 int _day;
};

写在类中时,类类型参数可以省略!

因为类中函数默认有this指针
指针this就代表了此类对象了!


3.2 特殊的赋值运算符

赋值运算符十分特殊
若我们不显示写,编译器会自动生成一个

所以赋值运算符只能写在类中
如果显示写在了类外的话
编译器会自动生成一个赋值函数
这就和你自己写的冲突了!

Date& operator=(const Date& d)
 {
 if(this != &d)
 {
     _year = d._year;
     _month = d._month;
     _day = d._day;
 }
 return *this;
 }

注:this是类对象的地址
*this就是类对象本身

赋值运算符返回类对象的原因是:
多次连续赋值

Date d1(2023,7,31);
Date d2;
Date d3;
d2 = d3 = d1;//连续赋值

拓展:

编译器自动生成的默认赋值运算符重载,以值的方式逐字节拷贝。

注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

总结:
如果类中未涉及到资源管理
赋值运算符是否实现都可以
一旦涉及到资源管理则必须要实现


3.3 前置++和后置++

对于++的运算符重载比较有争议
因为前置和后置是两种不同的函数!

C++为了区分前置和后置++
在后置++的函数中多加一个int
类型的参数来区别前置++

比如:

前置++:

Date& operator++();

后置++:

Date& operator++(int);

注意,虽然后置++多了一个参数
但是这个参数完全没用!
只用于区别前置++

++运算符的使用:

Date d1(2023,7,31);
//前置++
++d1;
//后置++
d1++;

虽然后置++多了一个参数
但是不需要我们显示传参
编译器会自动帮助我们传参!


3.4 运算符重载再理解

先看以下日期类代码:

class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
        _year = year;
        _month = month;
        _day = day;
 }
 bool operator==(const Date& d2)
 {
	  return _year == d2._year;
	      && _month == d2._month
	      && _day == d2._day;
 }
 Date& operator=(const Date& d)
 {
	 if(this != &d)
	 {
	     _year = d._year;
	     _month = d._month;
	     _day = d._day;
	 }
	 return *this;
 }
private:
 int _year;
 int _month;
 int _day;
};

现在定义类对象进行操作:

Date d1(2023,7,31);
Date d2(2023,7,30);
if(d1==d2)
{
	cout<<"d1和d2相同";
}
Date d3;
d3 = d1;

上面的代码中调用了两个运算符重载函数
operator=operator==

实际上调用 == 时是这样的:

在这里插入图片描述
调用 = 时是这样的:

在这里插入图片描述


4. 总结以及拓展

写运算符重载函数时,尽量使用引用传参

const Date& d

若传值传参,会调用拷贝构造,效率很低

有几个操作符不能被运算符重载:

  • .*
  • ::
  • sizeof
  • ?:
  • .

C++中拷贝构造和赋值运算符容易混淆

Date d1(2023,7,31);
Date d2 = d1;
Date d3;
d3 = d1;

d2=d1是调用了拷贝构造函数
d3=d1是调用了赋值运算符重载

关于更多拷贝构造和赋值的关系
可以参考这篇文章:

拷贝构造和赋值运算符重载


🔎 下期预告:类和对象(下) 🔍

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

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

相关文章

Approximating Wasserstein distances with PyTorch学习

https://github.com/dfdazac/wassdistance/tree/master 前置知识 Computational optimal transport学习 具体看到熵对偶的坐标上升那就行 L C ε ( a , b ) def. min ⁡ P ∈ U ( a , b ) ⟨ P , C ⟩ − ε H ( P ) \mathrm{L}_{\mathbf{C}}^{\varepsilon}(\mathbf{a}, \m…

Kafka系列之:记录一次Kafka Topic分区扩容,但是下游flink消费者没有自动消费新的分区的解决方法

Kafka系列之:记录一次Kafka Topic分区扩容,但是下游flink消费者没有自动消费新的分区的解决方法 一、背景二、解决方法三、实现自动发现新的分区一、背景 生产环境Kafka集群压力大,Topic读写压力大,消费的lag比较大,因此通过扩容Topic的分区,增大Topic的读写性能理论上下…

【RabbitMQ(day4)】SpringBoot整合RabbitMQ与MQ应用场景说明

一、SpringBoot 中使用 RabbitMQ 导入对应的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>配置配置文件 spring:application:name: rabbitmq-springbo…

云原生落地实践的25个步骤

一、什么是云原生&#xff1f; 云原生从字面意思上来看可以分成云和原生两个部分。 云是和本地相对的&#xff0c;传统的应用必须跑在本地服务器上&#xff0c;现在流行的应用都跑在云端&#xff0c;云包含了IaaS,、PaaS和SaaS。 原生就是土生土长的意思&#xff0c;我们在开始…

【Docker】Docker+Zipkin+Elasticsearch+Kibana部署分布式链路追踪

文章目录 1. 组件介绍2. 服务整合2.1. 前提&#xff1a;安装好Elaticsearch和Kibana2.2. 再整合Zipkin 点击跳转&#xff1a;Docker安装MySQL、Redis、RabbitMQ、Elasticsearch、Nacos等常见服务全套&#xff08;质量有保证&#xff0c;内容详情&#xff09; 本文主要讨论在Ela…

Spring Security OAuth2.0(7):自定义认证连接数据库

自定义认证连接数据库 首先创建数据库和用户表 CREATE TABLE t_user (id bigint(20) NOT NULL AUTO_INCREMENT,username varchar(64) DEFAULT NULL,password varchar(64) DEFAULT NULL,fullname varchar(255) DEFAULT NULL,mobile varchar(20) DEFAULT NULL,PRIMARY KEY (id)…

【vue】组件使用教训

组件使用 报错组件找不到 These dependencies were not found: 遇见的问题 在使用vue的时候&#xff0c;做了一个统计图的功能&#xff0c;引入了chart。 但是在运行项目的时候&#xff0c;直接报错启动不起来&#xff0c;报错内容是 告诉我依赖找不到&#xff0c;然后还试…

2023年华数杯建模思路 - 案例:退火算法

## 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 退火算法原理 1.1 物理背景 在热力学上&#xff0c;退火&#xff08;annealing&#xff09;现象指物体逐渐降温的物理现象&#xff0c;温度愈低&#…

医疗知识图谱问答 ——Neo4j 基本操作

前言 说到问答机器人&#xff0c;就不得不说一下 ChatGPT 啦。一个预训练的大预言模型&#xff0c;只要是人类范畴内的知识&#xff0c;似乎他回答得都井井有条&#xff0c;从写文章到写代码&#xff0c;再到解决零散琐碎的问题&#xff0c;不光震撼到我们普通人&#xff0c;就…

重生之我要学C++第一天

我重生了&#xff0c;今天开始带着上世纪的回忆重新学习C 目录 命名空间&#xff08;namespace&#xff09; 输入输出流 缺省参数&#xff08;默认参数&#xff09; 函数重载 命名空间&#xff08;namespace&#xff09; 新定义命名空间是C为防止对变量&#xff0c;函数&am…

2023年华数杯建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&#xff0…

版本控制和团队协作:前端工程化的关键要素

文章目录 版本控制系统介绍&#xff08;如 Git&#xff09;1. 分布式系统2. 分支管理3. 版本控制4. 快速和高效5. 社区和生态系统 分支管理和团队协作流程1. 主分支2. 功能分支3. 开发工作4. 合并到develop5. 发布准备6. 发布 持续集成与持续部署实践持续集成&#xff08;CI&am…

LeetCode·每日一题·2681. 英雄的力量·脑筋急转弯

题目 示例 思路 代码 static const int MOD 1e9 7; int cmp(const void *a, const void *b) {return *(int *)a - *(int *)b; } int sumOfPower(int* nums, int numsSize){qsort(nums, numsSize, sizeof(nums[0]), cmp);//排序long long ans 0, s 0;for (int x 0; x <…

C++ 虚函数详解(动态绑定)

动态绑定 虚函数的作用主要是实现了多态的机制。关于多态&#xff0c;简而言之就是用父类型别的指针指向其子类的实例&#xff0c;然后通过父类的指针调用实际子类的成员函数。我们在使用基类的引用&#xff08;指针&#xff09;调用虚函数时&#xff0c;就会发生动态绑定。所…

快速开发人脸识别系统Java版本

简介&#xff1a; 先说下什么是人脸识别系统&#xff1a;举个例子&#xff0c;公司门口有个人脸识别系统&#xff0c;员工站到门口&#xff0c;看着摄像头&#xff0c;大屏幕上会抓拍到你的人脸&#xff0c;然后和公司的员工照片库里的照片比对&#xff0c;比对成功就提示&…

哪些行业适合使用PDM系统

在现代数字化时代&#xff0c;PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;以其强大的功能和灵活的特性&#xff0c;成为各个行业提高生产效率和管理水平的得力工具。那么&#xff0c;哪些行业适合使用PDM系统呢&#xff1f;让我们一同深入…

Django使用用户列表的展示和添加

接着上一篇&#xff1a;https://blog.csdn.net/javascript_good/article/details/132027702 来实现用户表的查询和添加 1、创建数据库表 在models.py 中&#xff0c;增加UserInfo类&#xff0c;包括字段姓名、密码、年龄、账号余额、入职时间、所属部门、性别 verbose_name 就…

选读SQL经典实例笔记15_窗口函数

1. 分组 1.1. 把相似的行数据聚集在一起 2. SQL分组的定义 2.1. 数学上的“群”&#xff08;group&#xff09;定义为 (G, •,e)&#xff0c;其中G是一个集合&#xff0c;• 表示G的二进制运算&#xff0c;而e则是G中的成员 2.2. 一个SQL 分组须满足的两个定理 2.2.1. 对于…

计算机毕设 深度学习手势识别 - yolo python opencv cnn 机器视觉

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…

好用的Linux远程工具

你好&#xff0c;我是Martin&#xff0c;今天给大家介绍几款主流的远程工具。 远程工具介绍 关于远程连接的用户分类时这样的&#xff0c;通常需要进行远程连接的人有两类&#xff0c;一类是系统管理员&#xff0c;另一类是普通的用户。远程连接工具是一些可以让你通过网络连接…