【C++】运算符重载(日期类的实现)

news/2024/12/4 3:53:20/

【C++】运算符重载(日期类的实现)

  • 前言
  • 运算符重载
    • operator
      • 全局和类中
  • 日期类的实现
    • 成员变量的确定
    • 构造函数
      • 拷贝构造
    • 运算符重载部分
      • +=的重载
        • 思路
        • 实现
          • GETmonthday
          • operator+=
      • +的重载
        • 思路
        • 实现
      • -=的与-的重载
        • 实现
      • 各个比较运算符的重载
        • 实现
      • 前置++与后置++
        • 实现
      • (=)赋值运算符的重载
        • 实现
  • 后话

前言

博主在快写完类和对象(中)时,突然发现需要用到运算符重载的知识
但是运算符有很多运算符要进行重载,会导致文章的篇幅略长
所以就单独写了一个,另外,日期类是学习运算符重载比较好的一个例子,所以这里就用了日期类来讲解运算符重载

运算符重载

众所周知,对于内置类型我们可以直接使用运算符进行计算
int y=1, int x=1 x+y
这显然是可以的

那自定义类型可不可以直接用运算符来算呢?
比如
stack s1; stack s2; s1+s2

当然是不可以的

这不是显而易见吗
现在编译器也不至于聪明到能直接分辨出用户的自定义类型的运算。
要是编译器真的高级到这个程度,那就是更高级的语言了

但是类如果要进行加减乘除的时候又要进行函数的调用

如果能直接用加减乘除等操作符的话,那不是能大大增强函数的可读性吗?

所以就有了关键字

operator

运算符重载函数的形式为:
void(返回类型)operator<(需要重载的运算符)(参数)
例:

Date& operator+=(int x);

这里在使用operator进行重载时
有几个点需要注意:

1.不可以用operator重载一个原本没有意义的符号。

如:operator@

2.运算符重载必须有一个自定义类型的对象

因为本来运算符重置就是为了自定义类而创建
要是没有自定义类的对象参数
那还有啥意义。

在这里插入图片描述

这五个运算符不能进行重载。

4.讲了很多次
自定义类型深究到最后,也就只是内置类型

所以当我们编写运算符重载函数的时候:
内置类型的运算符含义不能进行改变

你想改也改不了。

全局和类中

这里来给大家提个问题,全局和类,有啥区别

1. 全局中的函数无法随意调用类中的私人成员

在这里插入图片描述
就会出现这样尴尬的场面(这里有解决方法,但是还是留在之后讲)

但是如果将运算符重载函数写在类中,就可以随意调用了。

2. 类中有this指针这个存在

这意味着运算符重载在全局和类中有不同的定义方式

上面我们看到了:

全局对象需要完全地写出参数

但是在类中的函数因为有this指针的隐形传参,所以不需要将全部参数写出来

class Date
{Date& operator+=(int day) {}
}

这里this指针将对象的地址传了过去

所以不需要像全局变量一样传一个类的对象形参

所以为了方便,还是尽量讲运算符重载函数写在类中。

日期类的实现

日期算是我们生活中常见的了。

成员变量的确定


年-月-日组成

所以我们这里针对成员变量的设计也是十分容易

class Date
{
private:int _year;int _month;int _day;
};

构造函数

还记得我们这里要创建构造函数的原因吗?

因为date类中全是内置类型
编译器自动生成的默认构造函数不会对内置类型变量进行设定。
所以遇到内置类型就要自己写构造函数了

这里就写个缺省的和无参的

//缺省Date(int year,int month,int day){_year = year;_month = month;_day = day;}//无参
Date()
{_year = 2023;_month = 5;_day = 24;
}

拷贝构造

因为拷贝构造属于构造函数里的,这里就直接把它放在这里了

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

运算符重载部分

这个部分算是正式进入我们的正题了

这里的函数我都默认放在类中进行实现的。

+=的重载

这里要实现的功能是:

日期+=天数 =>(返回)新日期

注意这个运算符号是+=,原本就是要对前一个值进行改变的。
所以我们这里就是对 原对象进行+=,然后返回原对象就好

这里理解起来很容易,但是要实现起来的话还是有点小繁琐的。

思路

想要得到日期+天数转化为日期。

1.首先要得到知道每个月的天数

2.将现在日期的天数和传参的天数相加

3.判断,现在天数是否大于这个月应有的天数

若大于:
扣除每个月对应的天数,然后月份+1

若小于:
则日期已经++完成,直接返回就好

实现

我们要知道所在的这个月的天数,所以我们需要设定一个函数专门用来返回当月的对应的天数

GETmonthday
int GETmonthday(int year, int month)
{static int arr[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 arr[month];
}

这里通过静态数组将每个月对应的天数存储下来
(因为这个函数在后续需要多次调用,所以使用静态数组,防止多次开辟空间影响效率)

通过传来的年月,来进行 闰年/平年 对应月 的判断
得到对应的天数后返回。

operator+=
//这里返回值用了引用,因为+=返回的就是原对象,函数结束后没有随着函数被销毁。
Date& operator+=(int day) 
{_day += day;//将天数和天数相加while (_day > this->GETmonthday(_year,_month))//当现有天数大于本月总天数,程序继续{_day -= GETmonthday(_year, _month);_month++;//天数减去这个月对应天数,当前月份++if (_month > 12)//当月份大于12时,年++,当前月份变为1{_month = 1;_year++;}}return *this;//返回改变后的天数
}

+的重载

思路

+和+=有啥区别?

+=对原对象进行了改变,并且返回的也是原对象

而+ 就不一样了:
不会对原对象进行赋值改变
并且返回的是:原对象与天数相加后的新对象

实现

这里搞懂了区别后就可以进行实现了

这里就会发现:
其实+=和+中间的对象与天数相加的思路一样
就是需要
1.返回新对象
2.原对象不能进行改变。

Date operator+(int day)
{
//用原对象进行了拷贝构造出了tmpDate tmp(*this);//将tmp进行+=(用的是之前实现的+=重载)tmp += day;
//返回tmpreturn tmp;
}

-=的与-的重载

这里其实思路和+=与+的重载相似,所以这里就不多讲了
因为这篇博客主要是想讲清不同运算符重载的实现

实现

//-=的实现
Date& operator-=(int day)
{_day -= day;while (_day <= 0){_day += GETmonthday(_year, _month);_month--;if (_month < 1){_month = 12;_year--;}}return *this;
}
}
//-的实现
Date operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}

各个比较运算符的重载

这里比较运算符算是最简单的运算符重载了

传入一个Date类对象
和this指针的本对象进行年月日比较
最后返回布尔值即可

实现

//<的实现
bool operator<(const Date& d)
{if (_year < d._year)return true;else if (_year == d._year && _month < d._month)return true;else if (_year == d._year && _month == d._month && _day < d._day)return true;return false;
}//大于的实现
bool operator>(const Date& d)
{return ((!(*this < d)) && (!(*this == d )));
}//等于的实现
bool operator==(const Date& d)
{return _day == d._day && _year == d._year && _month == d._month;
}
//大于等于的实现
bool operator>=(const Date& d)
{return !(*this < d);
}
//小于等于的实现
bool operator<=(const Date& d)
{return !(*this > d);
}
//不等于的是实现
bool operator!=(const Date& d)
{return !(*this == d);}

前置++与后置++

这里还是和之前一样,来搞清楚这两个的区别

前置++

直接对象+1,然后返回对象值即可

后置++
先返回对象的原值,然后对象值++
这里的实现思路肯定不是这样,return值了以后函数就结束,怎么还能++

所以这里就要用原对象创建新对象
对原对象++,返回创建的新对象

搞清楚思路了以后,接下来还有个问题

区分两者

还记的运算符重载的命名方式吗

void(返回类型)operator<(需要重载的运算符)(参数)

但是前置++和后置++,都是++运算符

所以定义时都是operator++

这就不好区分了

但是祖师爷在定义的时候就发现

自增运算符都是对单个对象的运算

所以传参时不需要传任何参数
(原对象this指针会进行传递)

那让编译器给前置运算符或者前置运算符
随便传一个参数不就可以识别了吗

所以选择了后置++

Date& Date::operator++()
//前置++
Date Date::operator++(int)
//后置++
//用户自己声明时,要在后置运算符加上int参数

实现

//前置++
Date& Date::operator++()
{return (*this += 1);
}//后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}

所以从上面的实现中可以看出

当我们用自定义类型的++时,最好使用前置++

因为后置++需要创建一个新对象,会影响效率。

同样对于内置类型前置++和后置++

虽然也大致是这样的原理
前置++比后置++更加有效率
但是因为内置类型拷贝成本比较低
所以没有特意去区分前置++和后置++的使用

(=)赋值运算符的重载

我们知道,赋值运算符也是默认成员函数之一
就是用户不写时,编译器会自己生成。

目的:将一个对象的值,赋值给另一个对象

实现

在这里插入图片描述

这个赋值构造函数可能看着没啥问题

但是别忘了,对于内置类型来说,赋值能这样做

x=y=c=a
能做到这样的连续赋值。

但是我们的这个实现方法不能进行连续赋值

原因就在于:
返回类型为空

所以这里需要把赋值后的原对象进行返回

Date& operator=(Date& d1)
{_year = d1._year;_day = d1._day;_month = d1._month;return (*this);
}

后话

这里其实还有很多内容没有去实现。

比如说
对象与对象的运算符
后置–与前置–
但是这里主要是想带大家学习一下重载运算符而已,
所以这个日期类就点到为止。


http://www.ppmy.cn/news/81456.html

相关文章

《Java设计模式学习》迭代器模式

在这里&#xff0c;包括在很多的书中&#xff0c;我们都会看到设计模式的Demo都很简单。但是我们在实际开发过程中&#xff0c;却往往很少用到类似的设计模式。 但是大家在看到这篇文章的时候&#xff0c;希望大家可以动手敲一下。也许在敲击的过程中&#xff0c;你能获取到一定…

创建本地LocalHost-SSL证书

mkcert 使用方法 mkcert 是一个开源工具&#xff0c;用于快速生成有效的本地开发证书。它可以帮助开发人员在本地环境中使用 HTTPS 加密来模拟真实的生产环境。 安装 首先&#xff0c;你需要安装 mkcert 工具。以下是在常见操作系统上安装的命令&#xff1a; macOS 使用 Homebr…

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→DVPP

1 数据预处理概述 1.1 典型使用场景 受网络结构和训练方式等因素的影响&#xff0c;绝大多数神经网络模型对输入数据都有格式上的限制。在计算视觉领域&#xff0c;这个限制大多体现在图像的尺寸、色域、归一化参数等。如果源图或视频的尺寸、格式等与网络模型的要求不—致时…

You Only Look Once:Unified,Real-Time Object Detection总结笔记

一、论文思想 1.将一个图像分成S*S个网格&#xff08;grid cell&#xff09;&#xff0c;如果某个object的中心落在这个网格中&#xff0c;则这个网络就负责预测这个object。 2.每个网格要预测B个bounding box&#xff0c;每个bounding box除了要预测位置之外&#xff0c;还要…

CVE-2023-27363 FOXIT PDF READER与EDITOR任意代码执行漏洞复现

目录 0x01 声明&#xff1a; 0x02 简介&#xff1a; 0x03 漏洞概述&#xff1a; 0x04 影响版本&#xff1a; 0x05 环境搭建&#xff1a; 文件下载&#xff1a; 0x06 漏洞复现&#xff1a; POC下载&#xff1a; 利用POC&#xff1a; RCE&#xff1a; 0x07 修复建议&a…

使用 ArcGIS 绘制地理位置图

目录 准备数据创建新的地图项目添加数据符号化地理数据添加地图元素和注释导出地理位置图地理位置图是一种用于展示地理数据的有力工具,而 ArcGIS 是一款功能强大的地理信息系统软件,提供了广泛的地理数据处理和可视化功能。本教程将介绍如何使用 ArcGIS 绘制地理位置图,以展…

xFormers安装使用

xFormers是一个模块化和可编程的Transformer建模库&#xff0c;可以加速图像的生成。 这种优化仅适用于nvidia gpus&#xff0c;它加快了图像生成&#xff0c;并降低了vram的使用量&#xff0c;而成本产生了非确定性的结果。 下载地址&#xff1a; https://github.com/faceb…

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-8

深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践-总结-8 类加载的过程加载验证1.文件格式验证2.元数据验证3.字节码验证4.符号引用验证 类加载的过程 加载 在加载&#xff08;Loading&#xff09;阶段&#xff0c;Java虚拟机需要完成以下三件事情&#xff1a; 通过一个…