c++阶梯之类与对象(中)< 续集 >

前文:

c++阶梯之类与对象(上)-CSDN博客

c++阶梯之类与对象(中)-CSDN博客


前言:

在上文中,我们学习了类的六个默认成员函数之构造,析构与拷贝构造函数,接下来我们来看看剩下的默认成员函数。 


目录

前文:

前言:

5. 赋值运算符重载

5.1 运算符重载

普通函数版 

运算符重载版 

 5. 2 赋值运算符重载

1. 赋值运算符重载格式

 2. 实例

注意: 

 5.3 特殊的运算符重载 前置++与后置++

 6. Date类的实现(多功能)

Date.h 

Date.cpp 

main.cpp 

补充知识:

7.const成员 

8. 取地址及const取地址操作符重载


5. 赋值运算符重载

5.1 运算符重载

我们知道函数重载,那么运算符重载又是什么?

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意
<  > 不能通过连接其他符号来创建新的操作符:比如operator@
<  > 重载操作符必须有一个类类型参数
<  > 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
 <  > 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
 <  >   .*  ::  sizeof  ?:  . 注意以上5个运算符不能重载。这个经常在笔试选择题中出

我们可以这样认为,运算符重载就是给运算符赋予一个新的含义,<专为自定义类型而生> 不过默认并不会改变运算符的基本逻辑。

我们来看看他在实际中的应用,我们实现两个Date类对象比较,1.是否相等 2. 是否小于

普通函数版 

我们可以看到,在这个版本里,代码编写者取名十分随意,那么当别人看到这段代码,内心的活动估计十分激昂。虽然在功能上很完美,但其代码却会让人抓耳挠腮。 

class Date
{
public:Date(int year = 2024, int month = 2, int day = 6){_year = year;_month = month;_day = day;}//Data类拷贝构造,赋值重载,析构都不需要自己实现bool compare1(const Date& d) //DateEqual {return _year == d._year&& _month == d._month&& _day == d._day;}bool compare2(const Date& d)//DateLess{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){return _day < d._day;}}return false;}private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 2, 7);Date d2(1997, 1, 1);cout << d1.compare1(d2) << endl;cout << d1.compare2(d2) << endl;return 0;
}

运算符重载版 

这一版本的代码可读性大大增强,还免去了起名的麻烦,同时在调用函数的时候,自定义类型也可以像内置类型一样简单快捷的使用运算符。

class Date
{
public:Date(int year = 2024, int month = 2, int day = 6){_year = year;_month = month;_day = day;}//Data类拷贝构造,赋值重载,析构都不需要自己实现bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}bool operator<(const Date& d){if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){return _day < d._day;}}return false;}private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 2, 7);Date d2(1997, 1, 1);return 0;
}int main()
{Date d1(2024, 2, 7);Date d2(1997, 1, 1);/*cout << d1.operator<(d2) << endl;cout << d1.operator==(d2) << endl;*/cout << (d1 < d2) << endl;//在编译器看来,等同于cout << d1.operator<(d2) << endl;cout << (d1 == d2) << endl;//在编译器看来,等同于cout << d1.operator==(d2) << endl;return 0;
}

 注意:因为 << 的运算符很高,因此在调用时需要加括号。

运算符重载并不会改变运算符的底层逻辑,因此对于双操作数运算符来说,左操作数就是函数的第一个参数,右操作数是第二个参数。

那么运算符重载能定义成全局的吗,是的可以,但是这样我们就不能访问类的私有成员。但将类成员公有化,如何保证封装性呢?

我们可以使用友元函数,或者定义成成员函数。 

 5. 2 赋值运算符重载

1. 赋值运算符重载格式

参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义 

 2. 实例

我们发现,所谓的赋值重载与之前学到的拷贝构造几乎是一个模子里刻出来的,既然他们一样,那我们还有必要学他吗?

这里有一个误区,他们的实现确实一样,但有一个不同点。

<  > 拷贝构造是对并不存在的对象进行拷贝构造,既有拷贝又有构造;

<  > 赋值重载是对已经存在的对象进行赋值(拷贝)。 

class Date
{
public:Date(int year = 2024, int month = 2, int day = 6){_year = year;_month = month;_day = day;}Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}bool operator<(const Date& d){if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){return _day < d._day;}}return false;}bool operator!=(const Date& d){return !(*this == d);}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;
};int main()
{Date d1(2024, 2, 7);Date d2(1997, 1, 1);int a = 10;int b = 20;Date d3 = d1;//拷贝构造,即Date d3(d1);Date d4;d4 = d1;//赋值重载return 0;
}

我们在汇编视角可以看的更加清晰。 

  

注意: 

赋值重载只能定义成类的成员函数,不能定义成全局函数。

<  > 编译器不支持定义为全局函数,如果定义成全局会报错。

<  > 定义成全局函数,那么类中没有实现,作为默认成员函数之一,编译器会自动生成一个默认赋值重载,那么当调用的时候就会产生冲突。

<  > 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

我们不写,编译器会自动生成,那么我们还需要写吗?

视情况而定,值拷贝对于Date类来说很适用,那么对于我们之前写的Stack类呢?他会造成不小的麻烦 。关于这一点,在c++阶梯之类与对象(中)-CSDN博客中的拷贝构造函数一节有详细讲解,感兴趣的宝子可以去看看。

 5.3 特殊的运算符重载 前置++与后置++

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递

Date operator++(int)//后置++,后置++使用后加1,因此返回的结果是自己本身,返回之后自身+1
{Date tmp = *this;_day+=1;return tmp;
}Date& operator++()//前置++,前置++使用前++,前置++返回的结果是自身+1
{_day++;return *this;
}

 6. Date类的实现(多功能)

在Date类实现常用运算符的重载,日期计算器,两日期的间隔等功能。

Date.h 

#include<iostream>
using namespace std;
class Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month);// 全缺省的构造函数Date(int year , int month , int day );// 拷贝构造函数Date(const Date& d);// 赋值运算符重载Date& operator=(const Date& d);// 析构函数~Date();// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载bool operator>(const Date& d);// ==运算符重载bool operator==(const Date& d);// >=运算符重载bool operator >= (const Date& d);// <运算符重载bool operator < (const Date& d);// <=运算符重载bool operator <= (const Date& d);// !=运算符重载bool operator != (const Date& d);// 日期-日期 返回天数int operator-(const Date& d);//打印void Print();private:int _year;int _month;int _day;};

Date.cpp 

#include"Date.h"
//获取每个月的天数
int Date::GetMonthDay(int year, int month)
{int monthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)){return 29;}return monthDays[month];
}
//全缺省构造函数
Date::Date(int year = 1997, int month = 1, int day = 1)
{_year = year;_month = month;_day = day;
}//拷贝构造
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}//赋值运算符重载
Date& Date::operator=(const Date& d)
{if (*this != d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}//析构
Date::~Date()
{;
}//日期+=天数
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}//日期+天数
Date Date::operator+(int day)
{Date tmp = *this;tmp += day;return tmp;
}//日期-=天数
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){_day += GetMonthDay(_year, _month);_month--;if (_month == 0){_year--;_month = 12;}}return *this;
}//日期-天数
Date Date::operator-(int day)
{Date tmp = *this;//拷贝构造tmp -= day;return tmp;}//后置++
Date Date::operator++(int)//后置++,后置++使用后加1,因此返回的结果是自己本身,返回之后自身+1
{Date tmp = *this;_day++;return tmp;
}//前置++
Date& Date::operator++()//前置++,前置++使用前++,前置++返回的结果是自身+1
{_day++;return *this;
}//后置--
Date Date::operator--(int)
{Date tmp = *this;_day --;return tmp;
}//前置--
Date& Date::operator--()
{_day--;return *this;
}//重载==
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}//重载!=
bool Date::operator!=(const Date& d)
{return !(*this == d);
}//重载<
bool Date::operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){return _day < d._day;}}return false;
}//重载<=
bool Date::operator<=(const Date& d)
{return (*this < d) || (*this == d);
}//重载>
bool Date::operator>(const Date& d)
{return !(*this <= d);
}//重载>=
bool Date::operator>=(const Date& d)
{return !(*this < d);
}//日期-日期=天数
int Date::operator-(const Date& d)
{Date max = *this, min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){min += 1;n++;}return n * flag;	
}//打印
void Date::Print()
{cout << _year << "/" << _month << "/" << _day << endl;
}

main.cpp 

#include"Date.h"int main()
{Date d1(2024, 2, 7);Date d2(1997, 5, 3);cout << (d1 - d2) << endl;d1 -= 10;d1.Print();d2 += 20;d2.Print();cout << (d1 < d2) << endl;Date d3(1, 1, 1);d1.Print();d3 = d1++;d3.Print();++d3;d3.Print();return 0;
}

Date的实现是对近期知识的总结,糅合了许多语法,并且难度也不大,值得一写。 

补充知识:

补充上述相关的简略友元与操作数顺序知识。 这里使用重载流插入操作符<< 来进行演示。

 我们根据所学的知识写了 << 的重载成员函数,但在使用的时候出现了问题。

我们之前说过, 运算符重载并不会改变运算符的底层逻辑,因此对于双操作数运算符来说,左操作数就是函数的第一个参数,右操作数是第二个参数。

因此按我们写的,应该这么使用

但很显然,这与我们预期的不符。成员函数隐含形参this,这个我们没有办法更改,因此只能将他实现为全局函数,然后更改函数参数的顺序。这里会用到友元,现在我们只需要知道友元的存在,不必深究。

虽然此时可以使用 << 输出Date类型了,但他只能输出一个,正常的 << 是可以连续流插入的。

这里的问题在于运算符的结合性,<< 是从左往右,cout<<d1 之后需要返回一个值,然后再调用 << 。我们做如下更改,

此时的流插入操作符重载和我们心目中的就一般无二了。 

7.const成员 

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

我们知道,成员函数中隐含的参数this指针是不允许显式写出来的,因此我们要将this指针const化就只能通过上面的方法。

请思考下面的几个问题:
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?

8. 取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容! 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用的EasyExcel表格处理-2(动态合并、自适应宽高)

EasyExcel官网&#xff1a;点击查看 1、动态合并单元格 此处主要根据自定义处理类ExcelFillCellMergeStrategy进行处理&#xff0c;具体内容可看代码注释。 1.1 前端调用controller PostMapping("/download/template")public void toDoExport(HttpServletResponse…

c#string方法对比

字符串的截取匹配操作在开发中非常常见&#xff0c;比如下面这个示例&#xff1a;我要匹配查找出来字符串数组中以“abc”开头的字符串并打印&#xff0c;我下面分别用了两种方式实现&#xff0c;代码如下&#xff1a; using System; namespace ConsoleApp23{ class Progra…

Android开发 button 按钮点击两次 响应onclick方法

问题 Android开发 button 按钮点击两次 响应onclick方法 详细问题 笔者xml代码 <!-- 一个按钮 --> <Button android:id"id/button1" android:layout_width"wrap_conten…

Rust 第一个rust程序Hello Rust️

文章目录 前言一、vscode 安装rust相关插件二、Cargo New三、vscode调试rustLLDB 前言 Rust学习系列。今天就让我们掌握第一个rust程序。Hello Rust &#x1f980;️。 在上一篇文章我们在macOS成功安装了rust。 一、vscode 安装rust相关插件 以下是一些常用的 Rust 开发插件…

相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

《Git 简易速速上手小册》第1章:Git 基础(2024 最新版)

文章目录 1.1 Git 简介&#xff1a;版本控制的演变1.1.1 基础知识讲解1.1.2 重点案例&#xff1a;协作开发流程优化案例&#xff1a;功能开发与分支策略 1.1.3 拓展案例 1&#xff1a;代码审查与合并1.1.4 拓展案例 2&#xff1a;冲突解决 1.2 安装和配置 Git&#xff1a;首次设…

Linux大集合

Linux Linux是什么&#xff1f; Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、多任务、 支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和 64位硬件。 Linux内核 是一个Linux系统…

51单片机之数码管显示表白数字篇

朝菌不知晦朔 蟪蛄不知春秋 眼界决定境界 CSDN 请求进入专栏 是否进入《51单片机专栏》? 确定 目录 数码管的简介 数码管引脚定义 数码管的原理图 74HC245 代码实现 静态数码管的显示 动态数码管的显示 数码管实现表白画面 数码管的简介 L…

力扣面试题 05.06. 整数转换(位运算)

Problem: 面试题 05.06. 整数转换 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.通过将两个数进行异或操作求出两个数中不同的位(不同的位异或后为二进制1); 2.统计异或后不同的位的个数(即异或后二进制为1的个数) 复杂度 时间复杂度: O ( 1 ) O(1) O(1) 空间…

挑战杯 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…

2.3_9 吸烟者问题

2.3_9 吸烟者问题 问题描述 问题分析 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要有三种材料&#xff1a;烟草、纸和胶水。三个抽烟者中&#xff0c;第一个拥有烟草、第二个拥有纸…

彩虹系统7.0免授权+精美WAP端模板源码

最低配置环境 PHP7.2 1、上传源码到网站根目录&#xff0c;导入数据库文件 2、修改数据库配置文件&#xff1a;/config.php 3、后台&#xff1a;/admin 账号&#xff1a; 4、前台用户&#xff1a;123456 密码&#xff1a;1234561

目标检测:2如何生成自己的数据集

目录 1. 数据采集 2. 图像标注 3. 开源已标记数据集 4. 数据集划分 参考&#xff1a; 1. 数据采集 数据采集是深度学习和人工智能任务中至关重要的一步&#xff0c;它为模型提供了必要的训练样本和测试数据。在实际应用中&#xff0c;数据采集的方法多种多样&#xff0c;每…

C语言--------指针(1)

0.指针&指针变量 32位平台&#xff0c;指针变量是4个字节&#xff08;32bit/84)--------x86 64位平台&#xff0c;指针变量是8个字节&#xff08;64bit/88)--------x64 编号指针地址&#xff1b;我们平常讲的p是指针就是说p是一个指针变量&#xff1b; ************只要…

VR全景技术可以应用在哪些行业,VR全景技术有哪些优势

引言&#xff1a; VR全景技术&#xff08;Virtual Reality Panorama Technology&#xff09;是一种以虚拟现实技术为基础&#xff0c;通过360度全景影像、立体声音、交互元素等手段&#xff0c;创造出沉浸式的虚拟现实环境。该技术不仅在娱乐领域有着广泛应用&#xff0c;还可…

操作系统透视:从历史沿革到现代应用,剖析Linux与网站服务架构

目录 操作系统 windows macos Linux 服务器搭建网站 关于解释器的流程 curl -I命令 名词解释 dos bash/terminal&#xff0c;(终端) nginx/apache&#xff08;Linux平台下的&#xff09; iis&#xff08;Windows平台下的&#xff09; GUI(图形化管理接口&#xff…

基于SpringBoot和PostGIS的震中影响范围可视化实践

目录 前言 一、基础数据 1、地震基础信息 2、全国行政村 二、Java后台服务设计 1、实体类设计 2、Mapper类设计 3、控制器设计 三、前端展示 1、初始化图例 2、震中位置及影响范围标记 3、行政村点查询及标记 总结 前言 地震等自然灾害目前还是依然不能进行准确的预…

日志报错 git -c dif.mnemonicprefix=false -c core.guotepath=false 解决方法

前言: 在进行下面操作前,必须确保,你是否安装了Git。 查看Git 在命令行窗口中输入`git --version`: 如果这个命令成功显示了Git的版本信息,这表明Git已经被安装。 1. 使用Sourcetree SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端…

vite项目配置根据不同的打包环境使用不同的请求路径VITE_BASE_URL,包括报错解决

vite环境配置可以看官方文档&#xff1a;环境变量和模式 | Vite 官方中文文档 创建环境配置文件 在项目根目录下面创建.env和.env.production文件&#xff0c;.env是开发环境使用的&#xff0c;.env.production是生产环境使用的。 .env文件&#xff1a; # 基本环境 VITE_APP…

MySQL篇----第七篇

系列文章目录 文章目录 系列文章目录前言一、水平分区二、分库分表之后,id 主键如何处理三、存储过程(特定功能的 SQL 语句集)前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你…