日期类是我们学习类和对象这部分知识的常客,本篇博客我们就对日期类成员函数进行全面总结
目录
一、一览Date.h函数声明
二、Date.cpp逐部分实现
一、流插入与流提取运算符重载
二、日期之间比较大小相等运算符重载
1. >
2. ==
3. >=
4. !=
5. <
6. <=
三、日期与天数运算操作符重载
1.获取某年某月的天数的函数
2.日期+=天数
3.日期+天数
4.日期-=天数
5.日期-天数
四、赋值运算符重载函数
五、日期本身运算符重载函数
1.前置++
2.后置++
3.前置--
4.后置--
5.日期-日期
六、取地址运算符重载
七、日期类细节总结
三、日期类代码汇总(声明定义分离)
Date.h
Date.cpp
Test.cpp
一、一览Date.h函数声明
我们将要实现的日期类关键函数分成6部分来展开讲解
#include<iostream>
using namespace std;
class Date
{//1.流插入与流提取运算符重载friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public://获取某年某月的天数int GetMonthDay(int year, int month) const;//默认构造函数Date(int year = 1, int month = 1, int day = 1);//打印函数void Print() const; //2.日期之间比较大小相等运算符重载 //>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;//3.日期与天数运算操作符重载//日期+=天数Date& operator+=(int day);//日期+天数(复用日期+天数)Date operator+(int day) const;//日期-=天数Date& operator-=(int day);//日期-天数Date operator-(int day) const;//4.赋值运算符重载函数Date& operator=(const Date& d);//5.日期本身运算符重载函数//前置++Date& operator++();//后置++Date operator++(int);//前置--Date& operator--();//后置--Date operator--(int);//日期-日期int operator-(const Date& d) const;//6.取地址运算符重载Date* operator&();const Date* operator&()const;private:int _year;int _month;int _day;
};
二、Date.cpp逐部分实现
一、流插入与流提取运算符重载
我们已经清楚,流插入与流提取操作符平时都是这么用的:
#include<iostream>
using namespace std;
int main()
{int n = 0;cin >> n;cout << n;
}
可以看到,cin与cout都是写操作符左边的,变量在右边
而成员函数默认的this指针都是第一个形参,这就导致要把运算符重载函数设定为成成员函数的话,cin与cout的使用习惯要改变一下;
#include<iostream>
using namespace std;
class Date
{
public:void operator>>(istream& in){in >> _year >> _month >> _day;}void operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日";}
private:int _year;int _month;int _day;
};
int main()
{Date a;a >> cin;a << cout;
}
但是这样书写cin与cout明显不符合使用习惯和价值,因此我们还是选择把流插入与流提取操作符重载成全局函数, 而全局函数又不能直接访问类中的私有成员,这时要用到友元函数的声明;
#include<iostream>
using namespace std;
class Date
{//有元函数的声明friend void operator>>(istream& in, Date& d);friend void operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};
void operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;
}
void operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";
}
int main()
{Date a;cin >> a;cout << a;
}
为了支持连续输入与输出,函数的返回值得改一改~
#include<iostream>
using namespace std;
class Date
{//有元函数的声明friend istream& operator>>(istream& in, Date& d);friend ostream& operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";return out;
}
int main()
{Date a;Date b;cin >> a >> b;cout << a << endl << b;
}
二、日期之间比较大小相等运算符重载
需要实现的运算有 > == >= != < <=
我们只需要具体实现前两个运算,然后其他函数复用前两个即可(充分体现this指针的价值)
1. >
#include<iostream>
using namespace std;
class Date
{
public:bool 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;}}
private:int _year;int _month;int _day;
};
2. ==
#include<iostream>
using namespace std;
class Date
{
public:bool operator==(const Date& d) const{if (_year == d._year && _month == d._month && _day == d._day){return true;}else{return false;}}
private:int _year;int _month;int _day;
};
3. >=
bool Date::operator>=(const Date& d) const
{return *this > d || *this == d;
}
4. !=
bool Date::operator!=(const Date& d) const
{return !(*this == d);
}
5. <
bool Date::operator<(const Date& d) const
{return !(*this >= d);
}
6. <=
bool Date::operator<=(const Date& d) const
{return !(*this > d);
}
三、日期与天数运算操作符重载
1.获取某年某月的天数的函数
数组保存了平年每个月的天数,闰年二月直接返回29天,其余情况就返回数组中某个月天数
int GetMonthDay(int year, int month)
{const static int MonthArray[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 MonthArray[month];
}
2.日期+=天数
1.直接把天数加到_day上,天满了,月++,月满了,年++;
2.为了支持day为负数的操作,复用一会要实现的 -=
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;
}
3.日期+天数
日期+天数 意思是日期本身不变,返回加之后的结果,复用+=即可
Date Date::operator+(int day) const
{Date tmp(*this); //拷贝一份放到类tmp中//等价于 Date tmp = *this;tmp += day;return tmp;
}
4.日期-=天数
1.让_day减去天数,day<=0,月--,若月==0,年--,月归为12,最后_day加上该年该月天数
2.为了支持day为负数的操作,复用 +=
Date& Date::operator-=(int day)
{if (day < 0){return *this += (-day);}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
5.日期-天数
Date Date::operator-(int day) const
{Date tmp(*this);tmp -= day;return tmp;
}
四、赋值运算符重载函数
Date& Date::operator=(const Date& d)
{//避免自己给自己赋值if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
五、日期本身运算符重载函数
1.前置++
Date& Date::operator++()
{*this += 1;return *this;
}
2.后置++
1.后置++--加一个int参数进行占位,跟前置++构成函数重载进行区分
2.后置++返回的是++之前的值,因此我们拷贝构造了tmp保存++之前的值,返回tmp
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
3.前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
4.后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
5.日期-日期
日期-日期可能是正数,也可能是负数,我们事先不知道谁大谁小,因此假定了一下~,利用变量flag标识最后的正负
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 n = 0;while (min != max){min++;n++;}return n * flag;
}
六、取地址运算符重载
Date* Date::operator&()
{return this; //return nullptr; ---不想被取地址
}
const Date* Date::operator&()const
{return this;
}
七、日期类细节总结
1.有的函数后面加了const,有的没有加const,取决于函数内部是否需要改变日期类,+=,-=等操作都是要改变日期类本身的,而+ - 等操作是不改变的
2.不需要在函数内部改变日期类的时候尽量都加const,否则很容易在调用时造成权限放大~
3.非const成员可以调用const函数,const成员不能调用非const函数;非const函数内部可以调用const函数, const函数内部不能调用非const函数
4.拷贝构造函数和赋值运算符重载函数本质是不一样的,都有存在的价值~
···拷贝构造函数是用一个已经存在的对象拷贝构造一个要创建的对象
···赋值运算符重载函数是把一个已经存在的对象赋值给另一个存在的对象
三、日期类代码汇总(声明定义分离)
Date.h
#include<iostream>
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) const;//默认构造函数Date(int year = 1, int month = 1, int day = 1);//打印函数void Print() const; //加const目的是为了解决权限放大问题//总结一下:只读函数可以加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;//<=bool operator<=(const Date& d) const;//日期+=天数Date& operator+=(int day);//日期+天数(复用日期+天数)Date operator+(int day) const;//日期-=天数Date& operator-=(int day);//日期-天数Date operator-(int day) const;//赋值运算符重载函数Date& operator=(const Date& d);//前置++Date& operator++();//后置++Date operator++(int);//前置--Date& operator--();//后置--Date operator--(int);//日期-日期int operator-(const Date& d) const;//函数之间的互相调用//非const可以调用const---权限缩小//const不能调用非const---权限放大//取地址运算符重载---日常自动生成的就可以//如果不想被取到地址,可以手动实现取地址运算符重载函数Date* operator&();const Date* operator&()const;//无法重载成成员函数//流插入运算符重载函数//void operator<<(ostream& out);//流提取运算符重载函数//void operator>>(istream& in);
private:int _year;int _month;int _day;
};//void operator<<(ostream& out, const Date& d);
//void operator<<(ostream& out, const Date& d);
Date.cpp
#include "Date.h"int Date::GetMonthDay(int year, int month) const
{const static int MonthArray[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 MonthArray[month];
}Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;//检查日期是否合法if (month < 1 || month >12 || day > GetMonthDay(year, month)){cout << "日期非法" << endl;}
}void Date::Print() const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}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
{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)
{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+(int day) const
{Date tmp(*this); //拷贝一份放到类tmp中//等价于 Date tmp = *this;tmp += day;return tmp;
}// += 复用 + (效率偏低) --- +=的实现 两次拷贝(调用+的两次拷贝) + 一次赋值
//Date Date::operator+(int day)
//{
// Date tmp(*this);
// tmp._day += day;
// while (tmp._day > GetMonthDay(_year, _month))
// {
// tmp._day -= GetMonthDay(_year, _month);
// ++tmp._month;
// if (tmp._month == 13)
// {
// ++tmp._year;
// tmp._month = 1;
// }
// }
// return tmp;
//}
//
//Date& Date::operator+=(int day)
//{
// 法一:
// //Date ret = *this + day;
// //*this = ret; //赋值重载
//
// //法二:
// *this = *this + day; //赋值重载
// return *this;
//}
/Date& Date::operator-=(int day)
{if (day < 0){return *this += (-day);}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator-(int day) const
{Date tmp(*this);tmp -= day;return tmp;
}//赋值运算符重载函数---返回值可以是void, 但最好是Date&, 这样才能支持连续赋值
Date& Date::operator=(const Date& d)
{//避免自己给自己赋值if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}//前置++
Date& Date::operator++()
{*this += 1;return *this;
}//后置++--加一个int参数进行占位,跟前置++构成函数重载进行区分
//本质后置++调用,编译器进行特殊处理
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;
}//日期-日期
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 n = 0;while (min != max){min++;n++;}return n * flag;
}//取地址运算符重载函数
Date* Date::operator&()
{return this; //return nullptr; ---不想被取地址
}
const Date* Date::operator&()const
{return this;
}//不太符合使用习惯的写法
流插入运算符重载函数
//void Date::operator<<(ostream& out)
//{
// out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//}
//
流提取运算符重载函数
//void Date::operator>>(istream& in)
//{
// in >> _year >> _month >> _day;
//}//有元函数写法
//流插入运算符重载函数
//void operator<<(ostream& out, const Date& d)
//{
// out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//}
流提取运算符重载函数
//void operator>>(istream& in, Date& d)
//{
// in >> d._year >> d._month >> d._day;
//}//支持连续的流插入与流提取
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
//流提取运算符重载函数
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
Test.cpp
#include "Date.h"
void TestDate1() //测试运算符重载函数
{Date d1(2023, 7, 23);Date d2(2023, 8, 1);cout << (d1 > d2) << endl; //等价于 d1.operator>(d2);cout << (d1 == d2) << endl; //等价于 d1.operator==(d2);cout << (d1 >= d2) << endl; //等价于 d1.operator>=(d2);cout << (d1 != d2) << endl; //等价于 d1.operator!=(d2);cout << (d1 < d2) << endl; //等价于 d1.operator<(d2);cout << (d1 <= d2) << endl; //等价于 d1.operator<=(d2);
}void TestDate2() //测试 += 与 +
{//+=Date d1(2023, 7, 23);Date ret1 = d1 += 150;ret1.Print();//+Date d2(2023, 8, 1);Date ret2 = d2 + 33; //等价于Date ret2(d7 + 33);ret2.Print();
}void TestDate3() //测试 -= 与 -
{//-=Date d1(2023, 7, 23);Date ret1 = d1 -= 33;ret1.Print();//-Date d2(2023, 1, 13);Date ret2 = d2 - 23;ret2.Print();
}void TestDate4() // 测试 += -= + - 负数
{Date d1(2023, 8, 1);Date ret1 = d1 += -100;ret1.Print();Date d2(2023, 8, 1);Date ret2 = d2 -= -100;ret2.Print();
}void TestDate5() //测试拷贝构造函数与赋值运算符重载函数
{//拷贝构造和赋值重载是有区别的//拷贝构造:一个已经存在的对象去初始化另一个要创建的对象Date d3(2023, 7, 31);Date d4(d3);//赋值重载:两个已存在对象进行拷贝Date d5(2023, 8, 1);Date d6;d6 = d5; //等价于d6.operator=(d5);//连续赋值d3 = d6 = d5; // d6 = d5 表达式的返回值是d6d3.Print();
}void TestDate6() //测试前置++,后置++,前置--,后置--
{Date d1(2023, 7, 31);Date ret1 = ++d1; //可以显式调用 Date ret1 = d1.operator++();d1.Print();ret1.Print();Date d2(2023, 7, 31);Date ret2 = d2++;//可以显式调用 Date ret2 = d2.operator++(int);d2.Print();ret2.Print();Date d3(2023, 7, 31);Date ret3 = --d3;d3.Print();ret3.Print();Date d4(2023, 7, 31);Date ret4 = d4--;d4.Print();ret4.Print();
}void TestDate7() //测试日期-日期
{Date d1(2023, 8, 1);Date d2(2004, 3, 19);cout << d1 - d2 << endl;
}void TestDate8() //测试权限问题
{const Date d1; d1.Print(); //权限平移Date d2;d2.Print(); //权限缩小
}void TestDate9() //测试取地址运算符重载函数
{Date d1(2023, 8, 1);cout << &d1 << endl; //编译器调用了取地址运算符重载const Date d2(2023, 8, 2);cout << &d2 << endl; //编译器调用了取地址运算符重载
}void TestDate10() //测试流插入与流提取重载函数
{测试流提取//Date d1(2023, 8, 1);cout << d1; //如何直接使用 << 输出自定义类型对象 ---运算符重载函数//d1 << cout;测试流插入//Date d2;//d2 >> cin;//d2 << cout;//上述写法虽然可以,但是不符合使用习惯和价值//友元函数~//Date d3;//cin >> d3;//cout << d3;//连续插入&连续提取Date d4;Date d5;cin >> d4 >> d5;cout << d4 << d5;
}int main()
{TestDate1();TestDate2();TestDate3();TestDate4();TestDate5();TestDate6();TestDate7();TestDate8();TestDate9();TestDate10();
}