作者:chlorine
专栏:c++专栏
赋值运算符重载(+)(+=)(++):实现完整的日期类(上)
我走的很慢,但我从不后退。
【学习目标】
- 日期(- -= --)天数重载运算符
日期-日期 返回天数- 对日期类函数进行优化(不符合常理的日期,负数,const成员)
- c++中重载输入cin和输出cout运算符
- const成员
首先我要对上面一篇类和对象(上)进行补充,上一篇主要内容
- '=' 赋值运算符重载
- '<' '=' '>' '<=' '>=' '!=' 重载运算符
- '前置++' '后置++’ 重载运算符
相信大家对于operator的运用以及重载运算符有了一定的了解。本次博客我会继续深化赋值运算符重载的内容,并对各函数进行优化处理。
目录
🔑'—' '-='重载运算符
🔑前置--,后置--
🔑日期-日期=天数
🔑+=(-=)负数 (优化)
🔑C++中重载输入cin和输出cout运算符
🌈重载输入cin运算符>>
🌈重载输入cout运算符<<
🔑不符合常理的日期(优化)
🔑const成员(优化)
🌈const对于+=和+
🌈const对于d1<d2?和d1>d2
🕶️完整代码
🚩Date.h
🚩Date.cpp
🚩test.cpp
🔑'—' '-='重载运算符
我们不只局限于对未来的计算,虽然以前的日子已经一去不复返。如果我们想知道100天是哪一天?该如何操作呢?上面的图片今天是2023年11月18日,一百天前是2023年8月10日,这是该如何计算呢?
11月已经被-100了,这里要+上的是10月份的天数。
计算过程如图所示。我们可以根据上面思路进行写代码。
主要思路:先将日-100,然后如果是负数就进入循环 ,月份-1,就可以得到在此基础上的上一个月的天数,然后我们还要考虑一个情况,就是如果月份=0,我们就要追溯到上一年的12月开始。代码如下:
Date &Date::operator-=(int day)
{
_day-=day;
while(_day<=0)
{
--_month;
if(_month==0)
{
_month=12;
--_year;
}
_day+=GetMonthDay(_year,_month);
}
return *this;
}
那么-重载运算符和+重载运算符一个道理,不能改变原来的值,就创建一个临时变量tmp,保留d1原来的值,然后返回tmp.
依旧使用-复用-=的方法.
Date Date::operator-(int day)
{
Date tmp(*this);
tmp -=day;
return tmp;
}
🔑前置--,后置--
前置--后置--和前置++后置++的用法是一样的
- 前置--:返回的减之后的值。
- 后置--:返回的减之前的值,需要用拷贝构造创建 一个临时变量tmp来保存原来的值。并且后置--重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递 。
//前置--返回的是减之后的值
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//后置--返回的是减之前的值
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
🔑日期-日期=天数
我想计算一下自己的生日和今天相差多少天?我该如何用代码实现呢?
第一种思路:
- 我们可以与小的日期和大的日期的年份设置成一样的,然后就可以利用 《月和日对齐,利用年判断润平年,闰年一年多少天,平年一年的多少天,相加)
- 然后算出了2003-10-29~~~2023-10-29相差多少天,然后 《年对齐,利用获得每个月的天数进行相加》
思路的俩个步骤,比较于直接减方便多了。
第二种思路:
- d1-d2 我们先假设d1是小min日期,d2是大max日期,如果我的猜测不对,就将min赋值给d2,max赋值给d1。我们要用flag=1或-1来记录,如果(小-大就得乘以-1)(大-小就得乘以1)
- 小的数如果一直++是可以与大的数持平的,我们就进行一个循环直到min=max的时候结束,过程中min一直++,用n来记下min一直+的次数,n的值就是天数。
代码如下:
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this<d)
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n*flag;
}
计算机的效率是可以完成循环多次的。
🔑+=(-=)负数 (优化)
图中不管是+还是-都是成立的对于day是正是负都是可以计算的。
day+= -100;
大家有没有想过如果day是负数呢?上面的代码运行的结果是什么呢?
这时候我们该如何优化呢?
d1+= -day 是不是相当于 d1-=day? 那么就可以复用-=重载运算符。
这样对嘛,d1+= -100,本来是看以前的日子的,怎么算到2024了?这里我们就要考虑到数学的知识了。 *this是d1 ,d1-=day ,day是-100,那么负负得正, 就成了d1+=day,所以我们需要在day前面加个负号,即d1-= -day。 -day=100,所以 d1-=100 与 d1+=-100 等式成立。
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month >= 13)
{
++_year;
_month = 1;
}
}
return *this;
}
同理:d1-= -100,看100天后的日期,那么就相当于负负得正,d1+=100,那么用到-=函数里就是 d1+=-day.
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month== 0)
{
_month = 12;
--_year;
}
_day+= GetMonthDay(_year, _month);
}
return *this;
}
🔑C++中重载输入cin和输出cout运算符
c++系统实现了一个庞大的类的库,如下图所示。其中ios为基类,其他类都直接或间接派生自ios类.
- cin和cout可以直接输入和输出内置类型(如int、double等)和部分标准自定义类型(如string等),原因是标准库已经将所有这些类型的输入和输出重载了,直接使用即可。
- 对于我们自定义的类型,如果想直接使用cin、cout来输入输出,需要自己重载>>和<<,否则不能直接使用。
在C++中,标准库本身已经对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数据的输入输出,但是输入输出的对象只能是 C++内置的数据类型(例如 bool、int、double 等)和标准库所包含的类类型(例如 string、complex、ofstream、ifstream 等)。如果自己定义了一种新的数据类型,需要用输入输出运算符去处理,那么就必须对它们进行重载。本节以Date类为例来演示输入输出运算符的重载。
cout << _year << "-" << _month << "-" << _day << endl;//输出形式
cin>>_year>>_month>>_day; //输入形式
cout是ostream类的对象,cin是istream类的对象,要想达到这个目标,就必须以全局函数(友元函数)的形式重载<<和>>,否则就要修改标准库中的类,这显然不是我们所期望的。
🌈重载输入cin运算符>>
以全局函数的形式重载>>,
//cin重载
istream& operator>>(istream& _in, Date& d)
{
_in >> d._year >> d._month >> d._day;
return _in;
}
注意:运算符重载函数中用到了Date类的private 成员变量,必须在Date类中将该函数声明为友元函数,如下例所示:
friend istream & operator>> (istream &_in,Date &d);
🌈重载输入cout运算符<<
同样地,也可以模仿上面的形式对输入运算符>>进行重载,让它能够输出,请看下面的代码:
//cout重载
ostream& operator<<(ostream& _out, const Date& d)
{
_out << d._year << "-" << d._month << "-" << d._day << endl;
return _out;
}
ostream表示输出流,cout是ostream类的对象。由于采用了引用的方式进行参数传递,并且也返回了对象的引用,所以重载后的运算符可以实现连续输出。
为了能够直接访问Date类的private成员变量,同样需要将该函数声明为complex类的友元函数:
friend ostream & operator<< (ostream &out, complex &A);
友元函数的声明:
我们有没有观察到
为什么输入重载里面的参数不加,而输出相反。
——输入年月日,如果输入流不能修改,那么就没办法进行输入了,输出在输入之后了,它保留着日期,是不能被修改的。
在这之前加coonst也是编译不过的,因为流入和流出的过程取它们其中的值也是需要改变它的状态值,所以输出输入都是不能加const的。 提取完了之后要改变其中内部的状态值的。
输入输出重载运算符代码如下:
//cin重载
istream& operator>>(istream& _in, Date& d)
{
_in >> d._year >> d._month >> d._day;
return _in;
}
//cout重载
ostream& operator<<(ostream& _out, const Date& d)
{
_out << d._year << "-" << d._month << "-" << d._day << endl;
return _out;
}
我们继续优化吧~
🔑不符合常理的日期(优化)
这个日期符合常理嘛?显然是不符合的。所有的对象是构造出来的,所以我们再构造的时候加一些检查~
- 第一种:直接打印出“非法日期”
- 第二种:直接断言报错(暴力法)
既然输出流对于不合理的日期进行了检查,那么输入呢?
输入非法日期之后,是可以直接通过的,那我们就得给流输入进行检查。
//cin重载
istream& operator>>(istream& _in, Date& d)
{
//第一种写法
_in >> d._year >> d._month >> d._day;
if (d._month > 0 && d._month < 13
&& d._day>0 && d._day <= d.GetMonthDay(d._year, d._month))
{
}
else
{
//cout << "非法日期" << endl;
assert(false);
}
return _in;
//第二种写法:
int year, month, day;
_in >> year >> month >> day;
if (month > 0 && month < 13
&& day>0 && day <= d.GetMonthDay(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
//cout << "非法日期" << endl;
assert(false);
}
}
一共俩种写法,第二种更灵活。
🔑const成员(优化)
如何让权限平移或者缩小呢?我们只需要给Date*this改成const Date*this就可以了。
我们不能动this的类型,因为它是隐含的是无法动的,那么如何将类型改成const使权限不放大呢?所以祖师爷就直接再函数后面加个const就可以了。
这里加个const 修饰的是*this。const Date* this我们之前就复习了const,这里const修饰的类型是Date,修饰的内容是*this。
重点:成员函数后面加const以后,普通和const对象都可以调用(权限的平移和权限的缩小)。
🌈const对于+=和+
那我们所写的所有成员函数都能加个const嘛?
——当然不是,要修改的对象成员变量函数不能加。
比如+=成员函数后面加个const。
_day===》this->day,const修饰的是this指向的内容,指向的内容都不能修改如何+=呢?——肯定不行。那么+呢?
+不会改变我自己,我们上面说了成员函数后面加const以后,普通和const对象都可以调用(权限的平移和权限的缩小)。(只要不改变成员变量都可以加)
所以我们看看还有什么可以加const?
🌈const对于d1<d2和d1>d2?
为什么d1<d2可以,d2<d1不可以呢?
这里就考虑到了权限的放大缩小平移问题了,d2是const显然传过去就是权限的放大,当然是不可以的。所以最好的方式就是给这些成员函数都加const。
结论:只要成员函数内部不修改成员变量,就应该加const,这样const对象和普通对象都可以调用。
这些成员函数都可以后面加const,因为按上面的结论,就是不修改内部成员变量。
🕶️完整代码
🚩Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
//友元函数的声明
friend ostream& operator<<(ostream& _out, const Date& d);
friend istream& operator>>(istream& _in, Date& d);
public:
int GetMonthDay(int year, int month)
{
int array[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;
}
else
{
return array[month];
}
}
void print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Date(int year = 2003, int month = 10, int day = 5);
Date& operator=(const Date& d);
bool operator==(const Date& d)const;
bool operator>(const Date& d)const;
bool operator>=(const Date& d)const;
bool operator<(const Date& d)const;
bool operator<=(const Date& d)const;
bool operator!=(const Date& d)const;
//日期+天数
Date operator+(int day) const;
Date& operator+=(int day);
// 日期-天数
Date operator-(int day)const;//自己不能改变。但是返回之后的结果
Date& operator-=(int day);
//前置++
Date&operator++();
//后置++
Date operator++(int);
//前置--
Date& operator--();
//后置--
Date operator--(int);
//日期-日期
int operator-(const Date& d)const;
private:
int _year;
int _month;
int _day;
};
🚩Date.cpp
#include<iostream>
using namespace std;
#include"Date.h"
Date::Date(int year, int month, int day)
{
if (month>0&&month<13
&&day>0&&day<=GetMonthDay(year,month))
{
_year = year;
_month = month;
_day = day;
}
else
{
//cout << "非法日期" << endl;
assert(false);
}
}
//赋值运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return (*this);
}
bool Date::operator==(const Date& d)const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator>(const Date& d)const
{
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;
}
else
{
return false;
}
}
bool Date::operator>=(const Date& d)const
{
return *this > d || *this == d;
}
bool Date::operator<(const Date& d)const
{
return !(*this>=d);
}
bool Date::operator<=(const Date& d)const
{
return !(*this > d);
}
bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
+=复用+
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// return *this;
//}
//
//Date Date::operator+(int day)
//{
// Date tmp(*this);
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// ++tmp._month;
// if (tmp._month >= 13)
// {
// ++tmp._year;
// tmp._month = 1;
// }
// }
// return tmp;
//}
//+复用+=
Date Date::operator+(int day)const
{
Date tmp(*this);
tmp += day;
return tmp;
}
//d1+=100
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month >= 13)
{
++_year;
_month = 1;
}
}
return *this;
}
//前置++返回的是加之后的值
Date &Date::operator++()
{
*this += 1;
return *this;
}
//后置++返回的是加之前的值
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
//前置--返回的是减之后的值
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//后置--返回的是减之前的值
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
Date Date::operator-(int day)const
{
Date tmp(*this);
tmp -=day;
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month== 0)
{
_month = 12;
--_year;
}
_day+= GetMonthDay(_year, _month);
}
return *this;
}
int Date::operator-(const Date& d)const
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this<d)
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n*flag;
}
//cin重载
istream& operator>>(istream& _in, Date& d)
{
//第一种写法
_in >> d._year >> d._month >> d._day;
if (d._month > 0 && d._month < 13
&& d._day>0 && d._day <= d.GetMonthDay(d._year, d._month))
{
}
else
{
//cout << "非法日期" << endl;
assert(false);
}
return _in;
//第二种写法:
int year, month, day;
_in >> year >> month >> day;
if (month > 0 && month < 13
&& day>0 && day <= d.GetMonthDay(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
//cout << "非法日期" << endl;
assert(false);
}
}
//cout重载
ostream& operator<<(ostream& _out, const Date& d)
{
_out << d._year << "-" << d._month << "-" << d._day << endl;
return _out;
}
🚩test.cpp
#include"Date.h"
void TestDate1()
{
Date d1(2003, 10, 5);
Date d2(2003, 11, 5);
int ret=d1 > d2;
int ret2 = d1 == d2;
int ret3 = d1 < d2;
int ret4 = d1 >= d2;
cout << ret3 << endl;
}
void TestDate2()
{
Date d1(2003, 11, 14);
Date d2=d1 +100;//运用拷贝构造创建一个临时变量tmp,将*this的值给tmp,*this的值不改变,tmp值改变,
//从而导致了d1+day,d1不变,tmp改变。返回d1原来的值。
//d1 +=100;
//d1 += 100;
d2.print();
/*d1++;
d1.print();
++d1;
d1.print();*/
}
void TestDate3()
{
Date d1(2023, 11, 17);
Date d2=d1 -100;
d2.print();
}
void TestDate4()
{
Date d1(2023, 11, 17);
//d1--;//后置--返回的是之前的值、
--d1;//前置--返回的是之后的值
d1.print();
}
void TestDate5()
{
Date d1(2023, 11, 17);
Date d2(2003, 10, 29);
cout << d1-d2<< endl;
cout << d2 - d1<< endl;
}
void TestDate6()
{
Date d1(2023, 11, 17);
d1.print();
/*d1 += -100;
d1.print();*/
d1 -= -100;
d1.print();
}
void TestDate7()
{
//Date d1(2023, 11, 17);
//d1 += 100;
流插入
//cout << d1;
//operator<<(cout, d1);
Date d1(2023, 11, 17);
Date d2(2003, 10, 29);
cin >> d1 >> d2;
cout << d1 << d2;
/*Date d4(2023, 13, 1);
cout << d4;*/
}
void TestDate8()
{
Date d4(2023,13,1);
cin>>d4;
cout << d4;
}
void TestDate9()
{
Date d1(2023, 11, 17);
const Date d2(2023, 11, 17);
d1 < d2;
d2 < d1;
//d1 + 100;//d1普通对象
//d2 + 100;//const对象
}
int main()
{
//TestDate1();
//TestDate2();
//TestDate3();
//TestDate4();
//TestDate5();
//TestDate6();
//TestDate7();
//TestDate8();
TestDate9();
return 0;
}
我走的很慢,但我从不后退。