目录
一、Date类:
通过理解并使用构造函数、析构函数、拷贝构造函数、运算符重载、赋值重载等,实现Date类。通过Date类,实现日期计算的功能。
二、模块化功能实现:
(1)实现日期比较
功能函数:
![]()
(2)实现日期计算:
![]()
![]()
功能函数:
(3)实现日期间隔计算
功能函数:
(4)实现输入、输出运算符重载
功能函数:
三、完整源码:
Date.h:
成员函数声明:
#pragma once #include<iostream> #include<cassert> //用什么,放什么 //防止自定义的与库中的冲突 using std::cout; using std::cin; using std::endl; 版本1:运算符重载函数:声明与定义分离 Date类 //class Date //{ //public: // // 获取某年某月的天数 // int GetMonthDay(int year, int month) // { // static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // int day = days[month]; // //判断闰年 // if (month == 2 // && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) // { // day += 1; // } // return day; // } // // // 全缺省的构造函数 // Date(int year = 1900, int month = 1, int day = 1) // { // if (year >= 1 && // month <= 12 && month >= 1 && // day >= 1 && day <= GetMonthDay(year, month)) // { // _year = year; // _month = month; // _day = day; // } // else // { // cout << "日期非法" << endl; // } // } // void Print() // { // cout << _year << "-" <<_month << "-" << _day << endl; // } // // 1.拷贝构造函数 // // d2(d1) // Date(const Date& d); // // // 2.赋值运算符重载 // // d2 = d3 -> d2.operator=(&d2, d3) // Date& operator=(const Date& d); // // 3.析构函数 // //~Date(); // // 4.日期运算 // // 日期+=天数 // Date& operator+=(int day); // // 日期+天数 // Date operator+(int day); // // 日期-天数 // Date operator-(int day); // // 日期-=天数 // Date& operator-=(int day); // // 前置++ // Date& operator++(); // // 后置++ // Date operator++(int); // // 后置-- // Date operator--(int); // // 前置-- // Date& operator--(); // // 日期-日期 返回天数 // int operator-(const Date& d); // // //5.运算符重载 // // >运算符重载 // bool operator>(const Date& d); // // ==运算符重载 // bool operator==(const Date& d); // // >=运算符重载 // bool operator >= (const Date& d); // // <运算符重载 // bool operator < (const Date& d); // // <=运算符重载 // bool operator <= (const Date& d); // // !=运算符重载 // bool operator != (const Date& d); // // //若使用inline函数: // //因为内联声明与定义不能分离,所以成员函数要成为内联,有两种方法 // //解决办法: // //1.在.h文件中:在类内声明,在类外定义 // //2.在.h文件中,直接在类内定义,默认为内联函数 // //原因: // //如果声明在.h,定义在.cpp,而测试在test.cpp // //则在test.cpp调用时链接不到 // //例:inline bool operator >= (const Date& d); // // // //private: // //这里只是声明,而声明不开辟空间 // //因此在类里面访问时,实参的第一个参数传给this指针, // //第二个参数传给d(可能是传引用或者传值传参) // //所以,还是在类里面访问成员函数,是公有的,有访问权限 // int _year; // int _month; // int _day; //}; 版本2:使用内联。将成员函数直接在类内定义 Date类 //class Date //{ //public: // // 获取某年某月的天数 // int GetMonthDay(int year, int month) // { // static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // int day = days[month]; // //判断闰年 // if (month == 2 // && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) // { // day += 1; // } // return day; // } // // // 全缺省的构造函数 // Date(int year = 1900, int month = 1, int day = 1) // { // if (year >= 1 && // month <= 12 && month >= 1 && // day >= 1 && day <= GetMonthDay(year, month)) // { // _year = year; // _month = month; // _day = day; // } // else // { // cout << "日期非法" << endl; // } // } // void Print() // { // cout << _year << "-" << _month << "-" << _day << endl; // } // // 1.拷贝构造函数 // // d2(d1) // Date(const Date& d); // // // 2.赋值运算符重载 // // d2 = d3 -> d2.operator=(&d2, d3) // Date& operator=(const Date& d); // // 3.析构函数 // //~Date(); // // 4.日期运算 // // 日期+=天数 // Date& operator+=(int day); // // 日期+天数 // Date operator+(int day); // // 日期-天数 // Date operator-(int day); // // 日期-=天数 // Date& operator-=(int day); // // 前置++ // Date& operator++(); // // 后置++ // Date operator++(int); // // 后置-- // Date operator--(int); // // 前置-- // Date& operator--(); // // 日期-日期 返回天数 // int operator-(const Date& d); // // //2.运算符重载(附用代码+使用内联(类内定义默认)) // //1.重载运算符== 在类内定义,无需再指定类域:Date:: // bool operator == (const Date& d) // { // return _year == d._year // && _month == d._month // && _day == d._day; // } // // //2.重载运算符 < // bool operator<(const Date& d) // { // if ((_year < d._year) // || (_year == d._year && _month < d._month) // || (_year == d._year && _month == d._month && _day < d._day)) // { // return true; // } // else // { // return false; // } // } // //3.重载运算符 > // bool operator>(const Date& d) // { // //1.直接逻辑 // /*if ((_year > d._year) // || (_year == d._year && _month > d._month) // || (_year == d._year && _month == d._month && _day > d._day)) // { // return true; // } // else // { // return false; // }*/ // //2.附用 // return !(*this <= d); // // } // // // 4.重载运算符 >= // bool operator >= (const Date& d) // { // return !(*this < d); // } // // // 5.重载运算符 <= // bool operator <= (const Date& d) // { // return *this < d || *this == d; // } // // 6.重载运算符 != // bool operator != (const Date& d) // { // return !(*this == d); // } // 7.重载运算符- 日期-日期 返回天数 // //int Date::operator-(const Date& d) // //{ // // // //} //private: // int _year; // int _month; // int _day; //}; //版本3:运算符重载函数:声明与定义分离 //Date类 class Date { public: //1.将GetMonthDay声明在类内.h,定义在类外.cpp int GetMonthDay(int year, int month); //2.将全缺省的构造函数声明在类内.h,定义在类外.cpp Date(int year = 1900, int month = 1, int day = 1); //3.将Print()声明在类内.h,定义在类外.cpp void Print() const; //被const修饰的以及Date类型的普通变量都可以调用这个含有隐式指针this的函数 // 4.拷贝构造函数 Date(const Date& d); // 5.赋值运算符重载 Date& operator=(const Date& d); // 6.析构函数 //~Date(); // 7.日期运算 // 日期+=天数 Date& operator+=(int day); // 日期+天数 Date operator+(int day); // 日期-天数 Date operator-(int day); // 日期-=天数 Date& operator-=(int day); //前置和后置,函数名相同,参数相同,语法规定加一个参数予以区别 //构成函数重载 // 例:++d1,返回的是++运算后的值 // 例:d1++ 返回++之前的值 // 前置++ Date& operator++(); // 后置++ //Date operator++(int i); //int是编译器传的 //Date operator++(int i=0); //不能写成缺省参数,因为没意义,参数只是区别前置和后置 //两个函数,当函数名相同,一个无参,一个带参,调用传参时,带参的不能写成全缺省的 //就像无参的构造函数与全缺省的构造函数不能同时存在是一样的,语法上可以,但调用上不能,无法区分 //实际上形参可以不写,不写意味着不使用所传的值,传值不需要就可以省去形参 //最终版 Date operator++(int); // 前置-- Date& operator--(); // 后置-- Date operator--(int); // 日期-日期 返回天数 // 日期-日期和日期-天数构成函数重载 int operator-(const Date& d); //8.运算符重载 // >运算符重载 bool operator>(const Date& d); // ==运算符重载 bool operator==(const Date& d); // >=运算符重载 bool operator >= (const Date& d); // <运算符重载 bool operator < (const Date& d); // <=运算符重载 bool operator <= (const Date& d); // !=运算符重载 bool operator != (const Date& d); //9.取地址及const取地址操作符重载 //这两个默认成员函数一般不用重新定义 ,编译器默认会生成。 //这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可, //只有特殊情况,才需要重载,比如不想让别人获取到指定的内容! (1)取地址操作符重载 //Date* operator&() //{ // return this; //} (2)const取地址操作符重载 //const Date* operator&()const //{ // return this; //} 如果不想被访问地址等情况,就自己写 (1)取地址操作符重载 //Date* operator&() //{ // return nullptr; //} (2)const取地址操作符重载 //const Date* operator&()const //{ // return nullptr; //} //10.I/O运算符重载 // 写成全局函数,但全局函数不能在.h定义,因为会在两个.cpp展开,编译会重定义报错 // 而类内定义,默认是内联,不会放在符号表,不会重定义报错 //void operator<<(std::ostream& out) //{ // cout << _year << "-" << _month << "-" << _day << endl; //} //void operator>>(std::istream& in) //{ //} //声明成友元函数 friend std::ostream& operator<<(std::ostream& out, const Date& d); friend std::istream& operator>>(std::istream& in, Date& d); private: int _year; int _month; int _day; }; 10.I / O运算符重载 解决私有对象不能访问的问题 1.借用函数Getyear,这相当于放成公有了,读写均可以了 2.使用友元函数 //void operator<<(std::ostream& out,const Date& d) //{ // cout << d._year << "-" << d._month << "-" <<d._day << endl; //} //void operator>>(std::istream& in,const Date& d) //{ // //}
Date.cpp:
成员函数功能实现:
#include "Date.h" //版本1 //运算符重载(直接逻辑): 1.重载运算符== //bool Date::operator == (const Date& d) //{ // return _year == d._year // && _month == d._month // && _day == d._day; //} // 2.重载运算符 < //bool Date::operator<(const Date& d) //{ // if ((_year < d._year) // || (_year == d._year && _month < d._month) // || (_year == d._year && _month == d._month && _day < d._day)) // { // return true; // } // else // { // return false; // } //} 3.重载运算符 > //bool Date::operator>(const Date& d) //{ // if ((_year > d._year) // || (_year == d._year && _month > d._month) // || (_year == d._year && _month == d._month && _day > d._day)) // { // return true; // } // else // { // return false; // } //} // 4.重载运算符 >= //inline bool Date::operator >= (const Date& d) //{ // //} // 5.重载运算符 <= //bool Date::operator <= (const Date& d) //{ // //} 6.重载运算符 != //bool Date::operator != (const Date& d) //{ // //} 7.重载运算符- 日期-日期 返回天数 //int Date::operator-(const Date& d) //{ // //} //版本2 //A.运算符重载(附用代码) //1.重载运算符== 当声明与定义分离,需要指定类域:Date:: //bool Date::operator == (const Date& d) //{ // return _year == d._year // && _month == d._month // && _day == d._day; //} // //2.重载运算符 < //bool Date::operator<(const Date& d) //{ // if ((_year < d._year) // || (_year == d._year && _month < d._month) // || (_year == d._year && _month == d._month && _day < d._day)) // { // return true; // } // else // { // return false; // } //} //3.重载运算符 > //bool Date::operator>(const Date& d) //{ // 1.直接逻辑 // /*if ((_year > d._year) // || (_year == d._year && _month > d._month) // || (_year == d._year && _month == d._month && _day > d._day)) // { // return true; // } // else // { // return false; // }*/ // 2.附用 // return !(*this <= d); // //} // // 4.重载运算符 >= //bool Date::operator >= (const Date& d) //{ // return !(*this < d); //} // // 5.重载运算符 <= //bool Date::operator <= (const Date& d) //{ // return *this < d || *this == d; //} // 6.重载运算符 != //bool Date::operator != (const Date& d) //{ // return !(*this == d); //} 7.重载运算符- 日期-日期 返回天数 //int Date::operator-(const Date& d) //{ // //} //版本3 //A.运算符重载(附用代码) //1.重载运算符== 当声明与定义分离,需要指定类域:Date:: bool Date::operator == (const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } //2.重载运算符 < bool Date::operator<(const Date& d) { if ((_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day)) { return true; } else { return false; } } //3.重载运算符 > bool Date::operator>(const Date& d) { //1.直接逻辑 /*if ((_year > d._year) || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day)) { return true; } else { return false; }*/ //2.附用 return !(*this <= d); } // 4.重载运算符 >= bool Date::operator >= (const Date& d) { return !(*this < d); } // 5.重载运算符 <= bool Date::operator <= (const Date& d) { return *this < d || *this == d; } // 6.重载运算符 != bool Date::operator != (const Date& d) { return !(*this == d); } 7.重载运算符- 日期-日期 返回天数 //int Date::operator-(const Date& d) //{ // //} //B. //获取某年某月的天数 int Date::GetMonthDay(int year, int month) { assert(year >= 0 && month > 0 && month < 13); //GetMonthDay会频繁调用,每次都会开辟数组,加上static //即使static有线程安全,但多个线程读影响小,写影响大 //static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int day = days[month]; //判断闰年 if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { day += 1; } return day; } //构造函数 Date::Date(int year , int month , int day ) { if (year >= 1 && month <= 12 && month >= 1 && day >= 1 && day <= GetMonthDay(year, month)) { _year = year; _month = month; _day = day; } else { cout << "日期非法" << endl; } } //打印函数 void Date::Print() const { cout << _year << "-" << _month << "-" << _day << endl; } //拷贝构造函数 // d2(d1) //日期类不需要写拷贝构造,这里写出来说明TestDate2中Date d3 = d1; Date::Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; } // 赋值运算符重载 // d2 = d3 -> d2.operator=(&d2, d3) //赋值运算符重载 Date& Date::operator=(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } //C.日期运算 // 日期+=天数 //d1+=100—>d1.operator+(day) d1自身改变 //连续d2+=d1+=100—>d1.operator+(day) //这个需要引用返回,因为d1改变了,出作用域返回仍存在 Date& Date::operator+=(int day) { //1.直接实现 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; 2.附用+ //*this = *this + day; //return *this; } // 日期+天数 //例:d1+100—>d1.operator+(day) d1自身不变 Date Date::operator+(int day) { //1.直接实现 使用拷贝构造,拷贝给ret,保证原有d1不改变,而要的结果改变d1+100 用一个已有的对象(*this就是d1)初始化一个新的对象ret //Date ret(*this); //ret._day += day; //while (ret._day > GetMonthDay(ret._year, ret._month)) //{ // ret._day -= GetMonthDay(ret._year, ret._month); // ret._month++; // if (ret._month == 13) // { // ++ret._year; // ret._month = 1; // } //} //return ret; //ret出作用域就不存在了,因此不能引用返回,而用传值返回 ret是局部对象,只在这个作用域内,如果传引用返回,访问ret的地址会形成野指针 ret是传值返回,传值返回是返回的临时拷贝, //2.附用+= Date ret(*this); ret += day; return ret; //总结:+和+=可以相互附用,但+附用+=,效率更高 //因为+会有两次拷贝复制,Date ret(*this);和return ret; } // 日期-天数 Date Date::operator-(int day) { //Print(); //附用-= Date ret(*this); ret -= day; return ret; } // 日期-=天数 //例:d1-=100 Date& Date::operator-=(int day) { //Print(); //解决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; } // 前置++ //例:++d1,返回的是++运算后的值 Date& Date::operator++() { *this += 1; return *this; } // 后置++ //例:d1++ 返回++之前的值 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) { int flag = 1; Date max = *this; Date min = d; if (*this < d) { min = *this; max = d; flag = -1; } int n = 0; while (min != max) { ++n; ++min; } return n*flag; } //10.I / O运算符重载 //解决私有对象不能访问的问题 //1.借用函数Getyear,这相当于放成公有了,读写均可以了 //2.使用友元函数 //加上返回值解决,连续输入输出 std::ostream& operator<<(std::ostream& out, const Date& d) { //cout是out的别名 out << d._year << "-" << d._month << "-" << d._day << endl; return out; } std::istream& operator>>(std::istream& in, Date& d) { //cin是in的别名 in >> d._year >> d._month >> d._day; Date(d._year, d._month, d._day); return in; }
test.cpp:
测试:
#include "Date.h" void TestDate1() { Date d1(2022, 07, 24); Date d2(2022, 02, 28); Date d3(2021, 02, 29); //可能是非法日期,看闰年 Date d4(2020, 05, 36); //非法日期 Date d5(2022, 02, 28); cout << (d1 < d2)<< endl; cout << (d1 > d2) << endl; cout << (d2 ==d5) << endl; cout << (d1 != d2) << endl; cout << (d2 <=d3) << endl; cout << (d1 < d3) << endl; cout << (d1 < d4) << endl; } void TestDate2() { Date d1(2022, 07, 24); d1.Print(); Date d2 = d1 + 100; //调用拷贝构造 d2.Print(); Date d3 = d1; //调用拷贝构造函数,等价于Date d3(d1);尽管使用了赋值符号= //Date d4; //d4 = d1; //这是赋值,两个已经存在的对象 } void TestDate3() { Date d1(2022, 07, 24); d1.Print(); Date d2 = d1 - 30; d2.Print(); d1 -= 30; d1.Print(); Date d3(2022, 07, 25); d3.Print(); d3 += 10000; d3.Print(); d3 -= 10000; d3.Print(); } void TestDate4() { Date d1(2022, 07, 24); d1.Print(); d1 -= -100; d1.Print(); //注:d1一直在改变,-=一次回到最初,两次实现从最初-= d1+= -100; d1 += -100; d1.Print(); } void TestDate5() { //注:d1、d2这些变量一直在改变 //1.++ Date d1(2022, 07, 24); d1.Print(); //2022-07-24 //++d1,返回的是++运算后的值 Date ret1 = ++d1; //前置++ 编译器会进行操作d1.operator++() ret1.Print(); //2022-07-25 d1.Print(); //2022-07-25 //d1++,返回++之前的值 Date ret2 = d1++; //后置++ 编译器会进行操作d1.operator++(0) 随便传一个数,这个参数其实没有意义,只是区分 ret2.Print(); //2022-07-25 d1.Print(); //2022-07-26 //2.-- Date d2(2022, 07, 01); d2.Print(); //2022-07-01 //--d2,返回的是--运算后的值 Date ret3=--d2; //前置-- 编译器会进行操作d2.operator--() ret3.Print(); //2022-06-30 d2.Print(); //2022-06-30 //d1--,返回--之前的值 Date ret4 = d2--; //后置-- 编译器会进行操作d2.operator--(0) ret4.Print(); //2022-06-30 d2.Print(); //2022-06-29 } // 日期-日期 返回天数 void TestDate6() { Date d1(2022, 07, 25); Date d2(2020, 02, 04); cout << (d1 - d2) << endl; cout << (d2 - d1) << endl; } //打印函数 //void Print(Date* const this) //const修饰的是this,this本身不能被改 //{ // cout << _year << "-" << _month << "-" << _day << endl; //} //void Print() const //C++为了解决const权限问题,在函数后面加上const,this就成为了const Date* 等价与void Print(const Date* const this) //{ // //编译器会自动加上this,即this->_year等,因此并不是所有函数都可以加,像this指向对象本身不修改的才可以加const // //例如:>、<、>=等可以在函数后加const,但++、--、-=、+=等不可以 // cout << _year << "-" << _month << "-" << _day << endl; //} void Func(const Date& d) { d.Print(); //其实是d1.Print(&d); &d取地址的类型是const,即const Date*传给Print的Date*,权限放大 //d被const修饰,&d取地址的类型是const Date*,即const 修饰*this //改成void Print() const后,const Date*传给Print的const Date*,权限不变 } void TestDate7() { Date d1(2022, 07, 25); d1.Print(); //其实是d1.Print(&d1);&d1取地址的类型是Date*,即Date*传给Print的Date* const,权限缩小 //这里&d1传给的this虽然被const修饰,但可以初始化,但this不能被修改 //Func(d1); (d1 + 100).Print(); } void Fun(const Date& d) { d.Print(); //其实是d1.Print(&d); &d取地址的类型是const,即const Date*传给Print的Date*,权限放大 //d被const修饰,&d取地址的类型是const Date*,即const 修饰*this //改成void Print() const后,const Date*传给Print的const Date*,权限不变 cout << &d << endl; } void TestDate8() { Date d1(2022, 07, 25); d1.Print(); Fun(d1); cout << &d1 << endl; } void TestDate9() { Date d1, d2; Date d3, d4; /*cout << d1; //d1插入到流 cout.operator<<(d1);*/ /*d1 << cout; //流插入到了d1 d1.operator<<(cout); //流插入到了d1*/ //cout重载 //operator<<(cout, d1); //cout << d1<<d2; //连续输出,重载函数要有返回值 //cin重载 //cin >> d3; cin >> d3 >> d4; cout << d3 << d4; //结合:从左到右 } int main() { //TestDate1(); //TestDate2(); //TestDate3(); //TestDate4(); //TestDate5(); //TestDate6(); //TestDate7(); //TestDate8(); TestDate9(); return 0; }
cout、cin重载:
cin和cout能自动识别类型,是因为库实现了函数重载,将常见的类型写为函数重载