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

💓博主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是调用了赋值运算符重载

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

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


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

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

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

相关文章

5G网络在中国已经普及了,政策支持加大5G投入力度,这意味着什么呢?

5G网络是新型基础设施的重要组成部分&#xff0c;中国5G商用牌照已发放四年多&#xff0c;目前发展得怎样了&#xff1f;最近&#xff0c;官方公布了最新数据&#xff0c;截至7月底&#xff0c;中国5G移动电话用户已达7亿户&#xff0c;5G基站累计达到293.7万个&#xff0c;5G覆…

HDFS介绍

目录 ​编辑 一、HDFS基础 1.1 概述 1.2 HDFS的设计目标 1.2.1 硬件故障 1.2.2 流式数据访问 1.2.3 超大数据集 1.2.4 简单的一致性模型 1.2.5 移动计算而不是移动数据 1.2.6 跨异构硬件和软件平台的可移植性 1.3 基础概念 1.3.1 块&#xff08;Block&#xff09; 1.3.2 复制…

Nodejs 第四章(Npm install 原理)

在执行npm install 的时候发生了什么&#xff1f; 首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装&#xff0c;并且排序规则.bin第一个然后系列&#xff0c;再然后按照首字母排序abcd等&#xff0c;并且使用的算法是广度优先遍历&#xff0c;在遍历依…

STM32F0实现IAP升级固件

好几年前写过一篇关于 STM32 bootloader 升级固件的博客&#xff0c;但是使用的芯片是 STM32 F4 系列&#xff0c;升级固件的方式是在外部 flash 的 fat32 文件系统中存入固件文件&#xff0c;reset 后通过特定按键进入 IAP 程序。 最近需要在 STM32 上实现同样的 IAP 功能&am…

过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程

Ⅰ、准备工作&#xff1a; 1、Git 查看&#xff1a; 其一、命令&#xff1a;git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git &#xff1a; A、命令 &#xff1a; sudo apt-get install git B、然后再输入虚…

HDFS的QJM方案

Quorum Journal Manager仲裁日志管理器 介绍主备切换&#xff0c;脑裂问题解决---ZKFailoverController&#xff08;zkfc&#xff09;主备切换&#xff0c;脑裂问题解决-- Fencing&#xff08;隔离&#xff09;机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…

宋浩概率论笔记(三)随机向量/二维随机变量

第三更&#xff1a;本章的内容最重要的在于概念的理解与抽象&#xff0c;二重积分通常情况下不会考得很难。此外&#xff0c;本次暂且忽略【二维连续型随机变量函数的分布】这一章节&#xff0c;非常抽象且难度较高&#xff0c;之后有时间再更新。

C++ 深拷贝和浅拷贝

虽然我们知道了拷贝构造函数&#xff0c;但是大多数简单程序中都不需要特别编写拷贝构造函数&#xff0c;隐含的拷贝构造函数足以实现对象间数据元素的一一对应复制。但是隐含的拷贝构造函数并不总是适用的&#xff0c;因为它完成的只是浅拷贝。 1.浅拷贝 【例】对象的浅拷贝…

面试热题(打家窃舍)

一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房屋存放金额的非负…

通话降噪算法在手机和IOT设备上的应用和挑战

随着电子产品的升级换代&#xff0c;用户对通话质量的要求也越来越高。通话降噪算法对通话质量起到了关键核心的作用。计算资源的提升使得深度学习模型在便携式的低功耗芯片上面跑起来了&#xff0c;器件成本降低让IoT设备开始使用骨导传感器&#xff0c;&#xff0c;那怎么样才…

Python接口自动化-requests模块之post请求

一、源码解析 def post(url, dataNone, jsonNone, **kwargs):r"""Sends a POST request.:param url: URL for the new :class:Request object.:param data: (optional) Dictionary, list of tuples, bytes, or file-likeobject to send in the body of the :cla…

【MySQL】学习汇总(完整思维导图)

用心打造超详细MySQL基础学习教程,文末附系列完整思维导图 内容导航 新手村 SQL入门(一)常见函数使用(二)约束(三)多表查询(四)事务(五) 进阶路 存储引擎(六) SQL性能分析 (七) 索引 (八) SQL优化(九) 视图(十) 存储过程(十一) 触发器(十二) 锁(十三) MySQL管理(十四)…

SpringBoot3---核心特性---2、Web开发I

星光下的赶路人star的个人主页 如果我们总是等待绝对的一切就绪&#xff0c;那我们将永远无法开始 文章目录 1、WebMvcAutoConfiguration1.1 生效条件1.2 效果1.3 WebMvcConfigure接口1.4 静态资源规则代码1.5 EnableWebMvcConfiguration源码1.6 为什么容器中放一个WebMvcConfi…

Flask项目打包为exe(附带项目资源,静态文件)

1.在项目根目录创建my_app.spec文件&#xff0c;内容如下&#xff1a; # -*- mode: python ; coding: utf-8 -*-block_cipher Nonea Analysis([server.py], # flask入口pathex[],binaries[], datas[("E:/**/templates","/templates"),("E:/**/s…

(7.28-8.3)【大数据新闻速递】《数字孪生工业软件白皮书》、《中国绿色算力发展研究报告》发布;华为ChatGPT要来了

【数字孪生工业软件白皮书&#xff08;2023&#xff09;】 近日&#xff0c;第七届数字孪生与智能制造服务学术会议成功举行&#xff0c;2023《数字孪生工业软件白皮书》在会上正式发布。《白皮书》在《Digital Twin》国际期刊专家顾问委员会指导下&#xff0c;由国家重点研发计…

物理量时空属性内禀律

一、物理量时空属性内禀律&#xff1a; 1、物理量具有内在的时空属性&#xff0c;对所有表达式具有时空强制性。 2、物理量时空属性的计算等级较表达式低1个数学等级。 二、物理量时空属性的表示&#xff1a; 空间直线L的时空属性表示为&#xff1a; d(L)(1s,0t) &#xff…

Spring 事务详解(注解方式)

目 录 序言 1、编程式事务 2、配置声明式事务 2.1 基于TransactionProxyFactoryBean的方式&#xff08;不常用&#xff0c;因为要为每一个类配置TransactionProxyFactoryBean&#xff09; 2.2 基于AspectJ的XML方式&#xff08;常用&#xff0c;可配置在某些类下的所有子…

「从零入门推荐系统」22:chatGPT、大模型在推荐系统中的应用

作者 | gongyouliu 编辑 | gongyouliu 提示&#xff1a;全文2.5万字&#xff0c;预计阅读时长2小时&#xff0c;可以先收藏再慢慢阅读。 我们在上一章介绍了chatGPT、大模型的基本概念、核心技术原理等基础知识&#xff0c;有了这些背景知识的铺垫&#xff0c;下面我们来介绍ch…

使用tinyxml解析和修改XML文件

首先要清楚XML文件包含哪些元素&#xff1a; 他是由元素、文本或者两者混合物组成。元素可以拥有属性&#xff0c;元素是指从开始标签到结束标签的部分。 <?xml version"1.0" encoding"UTF-8" ?> <books><book id"1001">&…

day50-springboot+ajax分页

分页依赖&#xff1a; <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> 配置&#xff1a; …