如果我们不实现,编译器默认帮我们实现一份!
//构造函数
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
//判断年月日是否合法
if (! ( (_year >= 0) && (month > 0 && month < 13) && ( day <= GetMonthDay(year,month) ) ) )
{
cout << "非法日期" << endl;
Print();//this->Print();
}
}
为了防止出现非法日期:如1月32日之类的,要写一个函数求出该月有多少天!
每次进来都要定义这个数组,这个数组不变,可以加static修饰
//用于求每个月的天数
//int ret = d1.GetMonthDay(2022, 2); 要通过对象去调用,成员函数的第一个参数默认是this指针!
int Date::GetMonthDay(int year, int month)
{
//每次进来都定义这个数组,所以可以放在静态区
//大小定义为13是为了让下标和月份对应的天数对应上
static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = monthDayArray[month];
//判断闰年
//把月的判断放在前面,效率高!
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day += 1;
}
return day;
}
//打印对象的信息
//d1.Print();
void Date::Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
如果想要知道一个日期+100天之后是什么时候?
这个月不够了,往下一个月进位.注意天数要减去原来月份对应的天数
要判断是否有年进位!
//+=运算符重载
// d1+=100
Date& Date::operator+=(int day)
{
_day += day;//先把天数加上
//考虑天数的进位
//GetMonthDay(_year, _month)求出这一年的这个月有多少天
while (_day >= GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
++_month;//月份进1位
if (_month == 13)
{
//年进位
_month = 1;
_year++;
}
}
return *this;
}
+= 返回的是加了之后的日期,并且会改变原来的对象.
出了作用域之后,this指针销毁了,但是*this空间还在,所以可以使用传引用返回.
+的逻辑和+= 同理
//+的运算符重载
//d1+10;
Date Date::operator+(int day)
{
//先拷贝构造一份
Date ret(*this);
ret += day;//调用上面的+=运算符重载
// 相当于ret.operator+=(day)
//返回这个对象的年月日
return ret;
}
此时不能改变原来的对象,如:i+10,i不改变.
所以先拷贝构造一份原来的对象,然后用这个临时对象复用+=的逻辑.
出了作用域之后,这个临时对象就销毁了.所以不能使用传引用返回,只能使用传值返回!返回的仍然是一个对象
++ 此时只有一个操作数->无参 (因为成员函数的第一个参数默认是this指针)
++之后返回的仍然是一个对象
为了让前置++和后置++进行区分:后置++增加了占位参数
,这个占位参数只能是int类型
,值是多少无所谓
//前置++
Date& Date::operator++()
{
//原对象自增,要写成+=1,日期类不能写成++
*this += 1;
return *this;
}
前置++:先自增,再使用值
this:调用该函数的对象的地址,*this:就是该对象.让该对象自增,然后返回
由于出了作用域,*this还在,所以可以使用传引用返回!
//后置++
Date Date:: operator++(int)
{
//先拷贝原对象,再返回
Date ret(*this);
//原对象自增,要写成+=1,日期类不能写成++
*this +=1;
return ret;
}
后置++:先使用值,再自增
先拷贝构造一份原对象,然后对原对象自增,然后返回这个临时拷贝对象
由于临时拷贝对象出了作用域就销毁了,所以不能使用传引用返回!这个和+的运算符重载类似
后置++和+的运算符重载都会调用两次拷贝构造 (第一次:拷贝构造临时对象 第二次:传值返回)
//>
//d1 > d2 ->d1.operator>(&d1,d2);
bool Date:: 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;
}
else
{
return false;
}
}
//==
//d1==d2 ->d1.operator==(&d1,d2);
bool Date:: operator==(const Date& d)
{
//年月日都相同才是相同的对象
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
//<
//d1= d);
}
//!=
//d1!=d2
bool Date:: operator!=(const Date& d)
{
//复用 == 运算符重载函数
return !(*this == d);
}
//>=
//d1>=d2
bool Date:: operator>=(const Date& d)
{
return *this > d || *this == d;
}
//<=
//d1<=d2
bool Date:: operator<=(const Date& d)
{
return *this == d || *this < d;
//return !(*this > d);
}
上述:
我们只实现了> 和 == 的运算符重载,其它都复用了
实现< 和 == 也可以进行复用
不仅仅是日期类可以这样子,所有的类要实现比较都可以用这种方式!
实现+和+=的时候,要进位.实现-和-=的时候 ->借位
进位的时候,要减的是当月的天数,因为当前月已经过完了
借位的时候,借的是上个月的天数.还要考虑跨年的情况->即出现0月的情况
相减出来得出的_day <=0 ->不合法,要获取上个月的天数进行借位,天数相加,直到/_day >0.如果不合法就继续处理
// -=
//d1-=10;
Date& Date::operator-=(int day)
{
_day -= day;
//_day <= 0 就是不合法需要处理
while (_day <= 0)
{
//往月借位
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
//加 上一个月的天数
_day += GetMonthDay(_year, _month);
}
return *this;
}
出了作用域,*this(d1)还在,所以可以用引用返回
出了作用域:this销毁了,但是*this没有销毁
//d1-10
Date Date:: operator-(int day)
{
//拷贝构造一份
Date ret(*this);
//临时对象复用-=的逻辑
ret -= day;
//返回临时对象
return ret;
}
先拷贝当前对象,然后复用-=运算符重载函数
出了作用域ret不在了,不能用引用返回!
测试自己的代码是正确:
-和-=复用的是一个逻辑,所以只需测试一个即可
当减一个负数的时候,会有问题!
原因:
_day -= day;
//_day 减去一个负数 -> +一个正数 不进入循环
while (_day <= 0)
解决办法:
判断一下day的值,如果是负数,就调用+=的运算符重载函数
//d1-=10;
Date& Date::operator-=(int day)
{
//传参是负数
if (day < 0)
{
return *this += -day;
}
_day -= day;
//_day <= 0 就是不合法需要处理
while (_day <= 0)
{
//往月借位
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
//加 上一个月的天数
_day += GetMonthDay(_year, _month);
}
return *this;
}
-不用处理,因为复用的是-=的逻辑
同理:+=也需要处理了!
//+=运算符重载
Date& Date::operator+=(int day)
{
//负数情况
if (day < 0)
{
return *this -= -day;
}
_day += day;//先把天数加上
//考虑天数的进位
//GetMonthDay(_year, _month)求出这一年的这个月有多少天
while (_day >= GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
++_month;//月份进1位
if (_month == 13)
{
//年进位
_month = 1;
_year++;
}
}
return *this;
}
+=一个负值,相当于减
日期 - 天数 ->返回值还是日期对象 日期 -日期 -> 返回值是整形
思路1:直接相减:多少年多少月多少天不好控制!月不齐(每个月的天数都不一样),还要考虑闰年平年
思路2:先比较出哪个日期更大,然后让小的日期不断++,加了多少次二者相等了,两个日期就相差多少天
先假设其中一个日期更大,令flag = 1
//日期 -日期
//offerDay - today
int Date::operator-(const Date& d)
{
//假设offerDay更大
Date max = *this;
Date min = d;
int flag = 1;
//offerDay是小的
if (*this < d)
{
max = d;
min = *this;
//更改flag的值
flag = -1;
}
int count = 0;
while (min != max)
{
++count;
++min;
}
//如果offerDay是大的:flag = 1,二者之差为正数
//如果offerDay是小的:flag = -1,二者之差为正数
//所以是count * flag
return count * flag;
}
方法:找一个起始的基准值 : 如 1900年1月1日 -> 星期1
然后利用 - 运算符重载 求出目标日期和基准值的差值
7天为一周 -> 周期为7 用差值%7就是星期几 ,返回的是 0 - 6
x % n => [0,n-1],所以可以定义一个数组,让下标和星期对应.
//今天是星期几
void Date::PrintWeekDay()
{
const char* arr[] = { "星期一","星期二" ,"星期三" ,"星期四" ,"星期五" ,"星期六" ,"星期天" };
//使用匿名对象的方式
//int count = *this - Date(1900, 1, 1);
Date start(1900, 1, 1);
int count = *this - start;
cout << "arr[count%7]" << endl;
}
// << 操作符重载
void Date::operator<<(ostream& out)
{
out << _year << "/" << _month << "/" << _day << endl;
}
注意:运算符重载里面,如果是双操作数的操作符重载,第一个参数是操作数,第二个参数是右操作数
所以按上述写法:调用时写成:d1 << cout ->相当于 d1.operator<<(&d1,cout);
写成成员函数的话,第一个参数默认就是this指针,第一个参数一定是我们的对象.如果想让cout在左边调用
->即 cout << d1 那就不能写在成员函数里 -> 在类外面写
void operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;//Date类的变量如果是私有的就不能在类外面进行访问
}
但是这样我们就要把成员变量改成公有的才能访问到,或者通过函数接口GetMonth…得出成员变量的值
另一种解决办法:使用友元函数->
注意:友元函数的声明要放在类里面
但是这也引出了新的问题:
如果想连续输出呢?cout << d1 << d2;
此时 cout << d1 返回类型为void void不能作为左操作数
所以可以写成:
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
return out;
}
由于流插入只是输出一下值为多少,不会改变对象的值,所以可以加const修饰对象
cout是全局对象,不会销毁
写在类成员函数时:双操作数的运算符重载时,规定第一个参数是左操作数,第二个参数是右操作数
void operator<<(ostream& out);
成员函数:默认第一个参数是默认的this指针,我们调用这个流插入重载时
d.opeartor<<(cout) <==> d< 实现成这样,是可以调用的.但是不符合使用习惯的解释含义 所以可以在全局外面写运算符重载函数,因为成员函数内:第一个参数就是默认的this指针不能改变 由于要修改对象,所以对象不能加const修饰 为了支持连续的输入,要有返回值. in就是cin的别名.cin是全局对象,出了作用域不销毁
>> 流提取运算符重载
istream& operator>>(istream& in,Date& d)
istream& operator>>(istream& in,Date& d)
{
cout << "请依次输入年月日,以空格间隔:>" << endl;
in >> d._year >> d._month >> d._day;
return in;
}
Date.h
#pragma once
#include
Date.cpp
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
//判断年月日是否合法
if (! ( (_year >= 0) && (month > 0 && month < 13) && ( day <= GetMonthDay(year,month) ) ) )
{
cout << "非法日期" << endl;
Print();//this->Print();
}
}
//用于求每个月的天数
int Date::GetMonthDay(int year, int month)
{
//每次进来都定义这个数组,所以可以放在静态区
//大小定义为13是为了让下标和月份对应的天数对应上
static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = monthDayArray[month];
//判断闰年
//把月的判断放在前面,效率高!
if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
day += 1;
}
return day;
}
//打印对象的信息
void Date::Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//+=运算符重载
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
_day += day;//先把天数加上
//考虑天数的进位
//GetMonthDay(_year, _month)求出这一年的这个月有多少天
while (_day >= GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
++_month;//月份进1位
if (_month == 13)
{
//年进位
_month = 1;
_year++;
}
}
return *this;
}
//+的运算符重载
Date Date::operator+(int day) const
{
//先拷贝构造一份
Date ret(*this);
ret += day;//调用上面的+=运算符重载
// 相当于ret.operator+=(day)
//返回这个对象的年月日
return ret;
}
// 前置++和后置++的运算符重载
//前置++
Date& Date::operator++()
{
//原对象自增,要写成+=1,不能写成++
*this += 1;
return *this;
}
//后置++
Date Date:: operator++(int)
{
//先拷贝原对象,再返回
Date ret(*this);
//原对象自增,要写成+=1,不能写成++
*this +=1;
return ret;
}
//d1>d2
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;
}
}
//d1 ==d2
bool Date:: operator==(const Date& d) const
{
//年月日都相同才是相同的对象
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
//d1!=d2
bool Date:: operator!=(const Date& d) const
{
//复用 == 运算符重载函数
return !(*this == d);
}
//d1
Test.cpp
#include"Date.h"
void Test1()
{
//普通的
Date d1(2022, 1, 18);
Date ret1 = d1 - 10;
ret1.Print();
//边界
Date ret2 = d1 - 18;
ret2.Print();
//跨年
Date ret3 = d1 - 500;
ret3.Print();
//跨年
Date ret4 = d1 - 1500;
ret4.Print();
//给负数的样例
Date ret5 = d1 - -100;
ret5.Print();
}
void Test2()
{
Date today(2022, 1, 19);
Date offerDay(2022, 9, 1);
cout <<(offerDay - today) << endl;
}
void Test3()
{
Date d1(2022, 1, 19);
Date d2(2022, 1, 20);
cout << d1 << d2;
}
void Test4()
{
Date d1;
Date d2;
cin >> d1 >> d2;
d1.Print();
d2.Print();
}
int main()
{
Test4();
return 0;
}