赞
踩
如果我们不实现,编译器默认帮我们实现一份!
//构造函数
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<d2
bool Date:: operator<(const Date& d)
{
return !(*this >= 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<<cout
实现成这样,是可以调用的.但是不符合使用习惯的解释含义
所以可以在全局外面写运算符重载函数,因为成员函数内:第一个参数就是默认的this指针不能改变
istream& operator>>(istream& in,Date& d)
由于要修改对象,所以对象不能加const修饰
为了支持连续的输入,要有返回值. in就是cin的别名.cin是全局对象,出了作用域不销毁
istream& operator>>(istream& in,Date& d)
{
cout << "请依次输入年月日,以空格间隔:>" << endl;
in >> d._year >> d._month >> d._day;
return in;
}
#pragma once #include<iostream> using namespace std; class Date { public: //友元函数 friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); //构造函数 Date(int year = 0, int month = 1, int day = 1); void Print() const; int GetMonthDay(int year, int month); //两个对象进行判断的运算符重载 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); //+的运算符重载 Date operator+(int day) const; // 前置++和后置++的运算符重载 Date& operator++();//前置++ //后置++为了和前置++进行区分,增加一个参数进行占位!二者构成了函数重载 Date operator++(int);//后置++ Date& operator-=(int day); Date operator-(int day) const; //日期 -日期 int operator-(const Date& d) const; //今天是星期几 void PrintWeekDay() const; private: int _year; int _month; int _day; };
#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<d2 bool Date:: operator<(const Date& d) const { return !(*this >= d); } //d1>=d2 bool Date:: operator>=(const Date& d) const { return *this > d || *this == d; } //d1<=d2 bool Date:: operator<=(const Date& d) const { return *this == d || *this < d; //return !(*this > d); } //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) const { Date ret(*this); ret -= day; return ret; } //日期 -日期 //offerDay - today int Date::operator-(const Date& d) const { Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int count = 0; while (min != max) { ++count; ++min; } return count * flag; } //今天是星期几 void Date::PrintWeekDay() const { const char* arr[] = { "星期一","星期二" ,"星期三" ,"星期四" ,"星期五" ,"星期六" ,"星期天" }; //使用匿名对象的方式 //int count = *this - Date(1900, 1, 1); Date start(1900, 1, 1); int count = *this - start; cout << "arr[count%7]" << endl; } ostream& operator<<(ostream& out, const Date& d) { out << d._year << "/" << d._month << "/" << d._day << endl; return out; } istream& operator>>(istream& in,Date& d) { cout << "请依次输入年月日,以空格间隔:>" << endl; in >> d._year >> d._month >> d._day; return in; }
#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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。