【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载)
=========================================================================
相关代码gitee自取:
C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
【C++初阶】四、类和对象
(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)-CSDN博客
=========================================================================
一 . 日期类的完善
此次日期类的成员函数,采用声明和定义(实现)分离的方式实现
成员函数声明和定义(实现)分离的好处:
将成员函数的声明写在头文件中,方便我们查看这个类中有哪些成员函数,
其具体实现再到成员函数实现文件(.cpp文件)中查看
(详细解释在图片的注释中,代码分文件放下一标题处)
Date构造函数 -- 全缺省构造函数优化
- 当实例化对象时,所给的初始化参数可能不合理(比如给了负数的日期),
需要进行特殊处理(报错) - 注意:
全缺省构造函数的缺省参数只能在声明或实现中给,
不能两边都给缺省参数,不然编译器不知道要使用哪边的缺省参数,
而且两边的缺省参数给得还可能不一样,
所以这里选择在声明中给缺省参数,实现时并没有给图示:
测试:
---------------------------------------------------------------------------------------------
Print函数 -- 打印日期函数
图示:
---------------------------------------------------------------------------------------------
GetMonthday“辅助”函数 -- 获取当前月份日期函数
- 因为考虑到各月份日期可能不一样,二月还需要考虑闰年还是平年,
所以可以单独写一个函数处理月份的情况,方便后续成员函数的实现 - assert断言:防止year(年份)或month(月份)传错
- 一年有12个月,定义一个有13个元素的数组,
不使用第一个元素,之后的12个元素分别为1~12月各个月的对应日期(平年) - 再单独判断2月的情况,如果year是闰年,再修改2月的日期为29天
- 最后通过数组下标返回对应月的日期
图示:
---------------------------------------------------------------------------------------------
operator==函数 -- “==”运算符重载函数
- 通过隐藏的this指针来依次判断两日期的年、月、日是否相同并返回结果
图示:
---------------------------------------------------------------------------------------------
operator!=函数 -- “!=”运算符重载函数
- 复用“==”运算符重载函数,对其结果取反,就能实现“!=”运算符重载函数了
图示:
---------------------------------------------------------------------------------------------
operator>函数 -- “>”运算符重载函数
- 先比较年份,“年大就大”;
如果年份相等,“月大就大”;
如果年份相等,月份也相等,“天大就大”;
大就返回true - 除此以外,就是两日期小于等于的情况了,返回false
图示:
---------------------------------------------------------------------------------------------
operator>=函数 -- “>=”运算符重载函数
- 复用“>”和“==”运算符重载函数即可实现">="运算符重载函数
图示:
---------------------------------------------------------------------------------------------
operator(const Date& y); //大于 //“>=”运算符重载函数: bool operator>=(const Date& y); //大于等于 //“ y._day) { return true; } //上面已经写出了所有大于的情况,执行到这说明是小于等于: return false; //返回false } //“>=”运算符重载函数: bool Date::operator>=(const Date& y) //大于等于 { //直接复用“>”和“==”运算符重载函数即可: return *this > y || *this == y; /* * *this即左值对象,y即右值对象 * 通过复用“>”和“==”运算符重载函数判断并返回结果即可, * “>=” --> “>” 或 “==” */ } //“= y); /* * *this即左值对象,y即右值对象 * 通过复用“>=”运算符重载函数即可, * 如果不是“>=”,那就是“”运算符重载函数即可, * 如果不是“>”,那就是“”(或“ GetMonthDay(_year, _month)) /* * 先通过GetMonthDay函数获得当月的天数, * 再比较相加后的天数是否超过了当月的天数, * 只要超过了则进行进位,进位到月: * (while循环到没超过为止) */ { //天数减去一轮当前月天数: _day -= GetMonthDay(_year, _month); //减去的一轮当前月天数进位到月中: ++_month; //如果当前月进位后超过了12个月: if (_month == 13) { //将一轮月份进位到年: ++_year; //将月份重置为1月: _month = 1; } } return *this; } //“+”运算符重载函数: Date Date::operator+(int day) { Date tmp(*this); /* * 为了实现加法,加了后不改变d1对象(+=才会改变) * 先通过*this(即d1)拷贝出一个tmp, * 对tmp进行加法操作就不会改变d1对象了 */ /* * 复用 “+=运算符重载” ,只要 += 到d1的拷贝tmp上即可, * 就不会改变到d1对象,通过tmp返回d1的加法结果: */ tmp += day; //通过tmp返回d1的加法结果: return tmp; /* * 这里tmp是d1的拷贝,出了函数就销毁了, * 所以需要传值返回拷贝一份回主函数 */ } “+”运算符重载函数: d1 + 100(整数) ,计算d1日期的100天后的日期: //Date Date::operator+(int day) // “+” 不会改变d1对象 // //为了防止拷贝返回,使用引用返回 //{ // Date tmp(*this); // /* // * 为了实现加法,加了后不改变d1对象(+=才会改变) // * 先通过*this(即d1)拷贝出一个tmp, // * 对tmp进行加法操作就不会改变d1对象了 // */ // // //思路:当月天数满了进位到月,月满了则进位到年: // // tmp._day += day; //先将要加的天数加到当前的天数上 // // //开始进位(如果需要的话): // while (tmp._day > GetMonthDay(tmp._year, tmp._month)) // /* // * 先通过GetMonthDay函数获得当月的天数, // * 再比较相加后的天数是否超过了当月的天数, // * 只要超过了则进行进位,进位到月: // * (while循环到没超过为止) // */ // { // //天数减去一轮当前月天数: // tmp._day -= GetMonthDay(tmp._year, tmp._month); // // //减去的一轮当前月天数进位到月中: // ++tmp._month; // // //如果当前月进位后超过了12个月: // if (tmp._month == 13) // { // //将一轮月份进位到年: // ++tmp._year; // // //将月份重置为1月: // tmp._month = 1; // } // } // //return tmp; ///* //* 这里tmp是d1的拷贝,出了函数就销毁了, //* 所以需要传值返回拷贝一份回主函数 //*/ //} “+=”运算符重载函数: //Date& Date::operator+=(int day) //{ // //复用“+”运算符重载函数: // *this = *this + day; // /* // * 这里的*this(左值对象)和day(右值对象), // * 都是已经存在的对象,把已经存在的对象赋值给 // * 另一个已经存在的对象上,需要使用到赋值运算符重载函数 // * // * 使用“+”运算符重载函数将day日期加到*this日期上, // * 再使用赋值运算符重载函数把加上的日期赋值给*this, // * 完成“+”运算符重载函数的复用 // */ // // //返回“+=”结果: // return *this; //} /* * 一: * 如果先实现了“+”运算符重载函数, * 可以复用“+”运算符重载函数实现“+=”运算符重载函数 * * 二: * 如果先实现了“+=”运算符重载函数, * 那就可以复用“+=”运算符重载函数实现“+”运算符重载函数 * * 第一种方法的“+”需要进行两次对象拷贝, * “+=”通过复用“+”实现,需要三次对象拷贝, * 所以该方法总共需要五次拷贝 * * 第二种方法的“+=”不需要进行对象拷贝, * “+”中需要两次对象拷贝,该方法总共需要两次拷贝 * * 所以还是通过第二种方法实现“+=”和“+”比较好, * 而且实现加法最好使用“+=”,而不是“+”, * 因为“+=”不需要进行拷贝,“+”需要拷贝两次对象 */ //“-=”运算符重载函数: Date& Date::operator-=(int day) { /* * 可能有坏蛋右值传了个负数过来, * 如:d1 -= -100; * 这时就要进行特殊处理了: */ if (day min * 那么 n*flag = 相差天数(正)*1 ,返回正日期 * 如果我们假设错了:max
Test.cpp -- 测试文件:
//包含日期类头文件: #include "Date.h" //优化全缺省构造函数: void TestDate1() { Date d1; //打印d1日期: d1.Print(); //全缺省构造函数的完善: //初始化的日期可能不合法(月份): Date d2(2023, 13, 1); d2.Print(); //初始化的日期可能不合法(日期): Date d3(2010, 2, 29); //2月可能没有29天 d3.Print(); } //测试 “-” 和 “+”(日期-+整型): void TestDate2() { //创建一个日期类对象:d1 Date d1(2023, 10, 24); d1.Print(); //测试“-”运算符重载函数: Date ret1 = d1 - 100; //隔几个月 ret1.Print(); //打印结果 Date ret2 = d1 - 10000; //跨越闰年 ret2.Print(); //打印结果 //测试“+”运算符重载函数: Date ret3 = d1 + 100; //隔几个月 ret3.Print(); //打印结果 Date ret4 = d1 + 10000; //跨越闰年 ret4.Print(); //打印结果 } //测试“前置++”和“后置++”: void TestDate3() { //创建一个日期类对象:d1 Date d1(2023, 10, 24); d1.Print(); ++d1; //d1.operator++() -- “前置++” d1.Print(); //2023/10/25 Date d2 = d1++; //d1.operator++(int) -- “后置++” d2.Print(); //先取到d1:2023/10/25,再++ d1.Print(); //2023/10/26 } //测试“日期-日期”运算符重载函数: void TestDate4() { //创建一个日期类对象:d1 Date d1(2023, 10, 24); d1.Print(); //创建一个日期类对象:d2 Date d2(2024, 2, 10); d2.Print(); //打印两日期相差的日期: cout
- 复用“>”和“==”运算符重载函数即可实现">="运算符重载函数
- 先比较年份,“年大就大”;
- 复用“==”运算符重载函数,对其结果取反,就能实现“!=”运算符重载函数了
- 通过隐藏的this指针来依次判断两日期的年、月、日是否相同并返回结果
- 因为考虑到各月份日期可能不一样,二月还需要考虑闰年还是平年,