本系列 C++ 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C++ 学习系列将分为三个阶段:基础篇、STL 篇、高阶数据结构与算法篇,相关重点内容如下:
- 基础篇:类与对象(涉及C++的三大特性等);
- STL 篇:学习使用 C++ 提供的 STL 相关库;
- 高阶数据结构与算法篇: 手动实现自己的 STL 库 及 设计实现高阶数据结构,如 B树、B+树、红黑树等。
学习集:
- C++ 入门到入土!!!学习合集
- Linux 从命令到网络再到内核!学习合集
运算符重载小专题
》!目前已更新:关系运算符的重载(点击跳转)
、输入输出运算符重载(点击跳转)
,解决的自定义类型 / 对象的比较与输入输出问题!自定义类型 / 对象
的运算问题!即:自增自减运算符重载及算术运算符重载
。本文中将以
+、-、+=、-=
为例,设计实现算术(赋值)运算符重载!【注意:在此前运算符重载说明篇中,提及过:运算符重载不能改变其本身意义!】
符号含义
:
+=
:加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数!-=
:减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数
对于我们的 Date 日期类而言,一般提供的接口会有:推算当前日期加减多少天后的日期问题!
此时就出现,如:2023-6-16 += 或 + 15天 之类的运算情形!显然使用原有的运算符是无法进行计算的!(如下图)
故,我们需要重载我们的运算符!
由于 + 或 - 可以基于 += 或 -= 来实现
,因此笔者优先实现对+= / -= 的重载
!
实现设计思路:
参数问题
:显然需要传递一个天数用于递推的运算。该值不需要改变,故可以使用 const 修饰。日期迭代计算问题
:由于涉及到日期的更迭,我们需要考虑的每个月的天数问题
!以及可能出现闰年二月的情形
!
关于:闰年问题!
判断闰年的方式(1582年后的计算准则):该年可以被 400 整除 或 概念可以被 4 整除但不能被 100 整除,如:1900年不是闰年!
关于闰年起源问题:(以上闰年计算法则是1582年后的计算规则!)
- 1582年以来公历的置闰规则:
- 普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
- 世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
- 1582年以前的惯例:四年一闰;如果公元 A 年的 A(正数)能被 4 整除,那么它就是闰年;如果公元前B年的B(正数)除以4余1,那么它也是闰年。
闰年判断函数实现(如下):
- 参数:年份
- 返回值:是 / 否
/* 闰年判断 */
bool IsLeapYear(int year) {
return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}
为了方便计算,笔者单独把获取月份天数设计为一个函数!
设计思路:
- 参数:年份、月份
- 返回值:int 类型(天数)
- 特殊处理:二月份默认设定为 28 天,遇到闰年,即返回值 +1;
优化处理:由于获取月份天数可能使用频率较高,建议使用数组存储数据(设计如下:注意注释解释),并设定为静态数组!(避免每次调用函数新建数组的性能开销!)。
代码实现如下:
/* 获取月份天数 */
int GetMonthDay(int year, int month) {
/* 设置为静态数组提升效率 */
/* 数组大小设置为 13 ,即为了直接使用 days[month] 访问到数据 */
static int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && IsLeapYear(year)) {
return days[month] + 1;
}
return days[month];
}
设计思路(注意点):
- 基本思路:总是在第几日上增加 / 减少天数实现推算日期,过程中注意判断月份更迭与年份更迭!
- 参数:推算所传递的天数。
返回值:日期的引用!
其他细节见注释!
+= 运算符重载(实现如下)
/* += 赋值运算符重载 */
Date& Date::operator += (const int day) {
_day += day; /* 直接递增天数 */
while (_day > GetMonthDay(_year, _month)) { /* 日期“合法性”判断 */
/* 若:天数大于本月的固定天数,则 天数 - 本月天数,并将月份 + 1 */
_day -= GetMonthDay(_year, _month); /* 天数更跌 */
_month += 1; /* 月份更迭 */
if (_month == 13) { /* 年份更迭 */
_month = 1;
_year++;
}
}
return *this;
}
-= 运算符重载(实现如下)
Date& Date::operator -= (const int day) {
_day -= day;
while (_day < 1) { /* 判断天数合法性 */
if (_month == 1) { /* 特殊点:年份更迭 */
_month = 13; /* 注意此处:13,为的是后续统一的月份更迭计算 */
_year--;
}
_day += GetMonthDay(_year, _month - 1);
_month--;
}
return *this;
}
结合上一篇文章中提及,提高代码复用性,此处可以直接使用已有的 += 或 -= 实现 + 或 - 运算符的重在!
+ 运算符重载(实现如下,注意注释提示)
注意返回值:不是引用!
/*
在该函数中,不会去修改 Date 对象本身,
而是返回一个新的对象,
函数外部可以赋值给原对象或新对象!
*/
Date Date::operator+(const int day) const
{
// Date ret(*this); /* 拷贝构造 */
Date ret = *this;
ret += day;
return ret;
}
+ 运算符重载(实现如下,注意注释提示)
注意返回值:不是引用!
/*
在该函数中,不会去修改 Date 对象本身,
而是返回一个新的对象,
函数外部可以赋值给原对象或新对象!
*/
Date Date::operator-(const int day) const
{
// Date ret(*this); /* 拷贝构造 */
Date ret = *this;
ret -= day;
return ret;
}
- 关于 * 、/、*=、/= 重载设计与实现,根据前文内容,照葫芦画瓢即可!
在 C/C++ 中,我们可以使用
++ / --
来对我们定义的数据变量进行自增自减操作!这俩运算符可谓是新人学习的噩梦,尤其是大学生期末考试试卷中,那没有任何实际意义的自增自减运算符组合求值问题。【基本使用如下图所示!】
在以往的学习中,可能大家或多或少听说过:
前置自增自减 效率高于 后置自增自减
。【当然在现在的编译器下肯定是几乎没有区别的,早已被优化】
已知,
自增自减运算符分为:前置 和 后置
,主要区别就是:
- 前置是:优先自身运算后再参与到其他运算;
- 后置是:先参与其他运算之后,再自身运算!
前置后置的
++ / --
实际使用中,我们书写就是前置和后置的区别,但是在重载的时候我们该怎么办?
函数写法:使用函数重载!
重载函数的区分方式
:使用一个参数来区别!方案:前置使用无参形式,后置使用含参形式
【注:实际实现后,我们不需要去显示传递后置实现过程中的那个参数!编译器会自动辨别为自增自减运算符重载的区别参数】函数返回值:对象本身(引用返回)
设计代码如下(注意复用代码):
前置 ++ ,即先 +1 再返回!
前置 -- ,即先 -1 再返回!
/* 前置 ++ */
Date& Date::operator++()
{
return *(this + 1);
}
/* 前置 -- */
Date& Date::operator--()
{
return *(this - 1);
}
后置 ++ ,即先返回,再运算!
(借助:临时对象)后置 -- ,即先返回,再运算!
(借助:临时对象)
/* 后置 ++ */
Date& Date::operator++(int)
{
Date ret(*this);
*this += 1;
return ret;
}
/* 后置 -- */
Date& Date::operator--(int)
{
Date ret(*this);
*this += 1;
return ret;
}
1. 【C++ 基础篇:21】:friend 友元四连问:什么是友元?友元类?友元函数?什么时候用友元?
2. 【C++ 基础篇:22】:类的 const 对象 与 const 成员函数/方法 以及 类中涉及 const 的常见问题!
3. 【C++ 基础篇:23】:【重要模板】关系运算符重载的设计与实现: [ > 、 < 、 >= 、 <= 、 != 、 == ] 重载【以 Date 时间类为例】