目录
日期+天数
需要考虑的几个问题
1.天数加在日上,有可能会溢出,需要进位
2.对月进位,也有可能导致月会溢出,需要进位
3.对年进位,需要考虑是否为闰年
代码设计
取得指定月的天数GetMonthDay函数
方法1:if判断或switch/case
方法2:查表
版本1
版本2
operator+
初始化临时对象
进位的处理
完整代码
测试代码
提问
operator+=
完整代码
测试代码
代码复用
1.+复用+=
2.+=复用+
提问
+复用+=调用拷贝构造函数次数如下:
+=复用+调用拷贝构造函数次数如下:
练习
日期+天数
日期+天数:显然需要重载"+",操作:?年?月?日+天数==?年?月?日
需要考虑的几个问题
1.天数加在日上,有可能会溢出,需要进位
2.对月进位,也有可能导致月会溢出,需要进位
3.对年进位,需要考虑是否为闰年
闰年判断参见15.【C语言】初识操作符 下文章
口诀:四年一闰,百年不闰,四百年又闰
if ((year%4==0 && year%100!=0) || (year%400==0))
{//do_something
}
代码设计
取得指定月的天数GetMonthDay函数
天数加在日上,有可能会溢出,需要进位则需要考虑每一个月最多多少天,而且二月份比较特殊(闰年2月有29天,非闰年2月有28天),可以写一个函数来取得每个月的天数
方法1:if判断或switch/case
if (month==1)
{//......
}
else if (month==2)
{//......
}
else if (month==3)
{//......
}
//.......
else
{//......
}
或者使用switch/case
switch (month)
{case 1:case 2://......case 3:
}
无论使用if还是switch/case,代码冗长,可读性差
方法2:查表
这个思想其实是哈希表思想
int day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//默认2月是28天,之后单独判断
(注:day[0]可以为任何数,仅起到占位的作用)
使用下标访问的方法:day[month]来取得指定月的天数
版本1
int GetMonthDay(int year,int month)
{int day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//默认2月是28天,之后单独判断if (((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) && month == 2){return 29;}return day[month];
}
提问:版本1代码有没有可以优化的地方?
答:
优化1:day数组为局部数组,每次调用该函数,程序都会创建day数组,如果多次调用,该步骤会消耗大量时间,可以尝试使用全局数组或者将数组放到静态区上
优化2:if判断的改进
可以利用短路运算的特点,将第二个条件month==2前置,月份不为2就不判断是否为闰年,节省时间
版本2
int GetMonthDay(int year,int month)//可以不使用引用返回
{//数组放到静态区上,只创建一次static int day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//默认2月是28天,之后单独判断if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){return 29;}return day[month];
}
operator+
算法:天满了向月进位,月满了向年进位.显然需要使用循环
要点:
1.先加到天上,再考虑进位的问题
2.operator+是不能改变原对象的,需要使用临时对象tmp,一旦使用临时对象tmp就不能引用返回,只能传值返回(避免传引用返回错误,牺牲一点时间)
3.区分operator+和operator+=
初始化临时对象
可以使用构造函数来初始化tmp,如下:
Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;//......
}
进位的处理
while (tmp._day > GetMonthDay(tmp._year,tmp._month))
{tmp._day -= GetMonthDay(tmp._year,tmp._month);tmp._month++;if (_month == 13){tmp._year++;tmp._month = 1;}
}
完整代码
Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year,tmp._month)){tmp._day -= GetMonthDay(tmp._year,tmp._month);tmp._month++;if (_month == 13){tmp._year++;tmp._month = 1;}}return tmp;//注意返回的不是*this!
}
测试代码
#include "Date.h"
int main()
{Date d1(2025, 3, 25);Date d2=d1 + 100;d2.Print();return 0;
}
运行结果:
提问
Date d2=d1 + 100;是初始化还是赋值?
回顾CD17.【C++ Dev】类和对象(8):赋值运算符文章的知识点:
1.赋值运算符重载:已经存在的两个对象之间的拷贝(如d3 = d1)
2.构造函数:用一个已经存在的对象去初始化另一个对象
显然Date d2=d1 + 100是构造函数执行的,因此为初始化
也可以手动打印测试信息:
修改构造函数和拷贝构造函数:
Date::Date(int year , int month, int day)
{cout << this <<" Date::Date(int year , int month, int day)" << endl;_year = year;_month = month;_day = day;
}Date::Date(const Date& x)
{cout << this<< " Date::Date(const Date& x)" << endl;_year = x._year;_month = x._month;_day = x._day;
}
调用哪个函数就打印哪个函数的信息
测试以下代码:
#include "Date.h"
int main()
{Date d1(2025, 3, 25);cout << "d1 address:"<< & d1 << endl;Date d2 = d1 + 100;cout <<"d2 address:"<< &d2 << endl;return 0;
}
运行结果:
初始化d2时调用的是拷贝构造函数,因为拷贝构造也是构造!
operator+=
可以在operator+的基础上进行代码复用(代码复用的思想参见CD18.【C++ Dev】类和对象(9)(声明和定义分离的写法以及代码复用)文章)
完整代码
按照之前在CD17.【C++ Dev】类和对象(8):赋值运算符文章中讲过复制运算符的格式,注意要返回*this,返回类型为Date&
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
测试代码
#include "Date.h"
int main()
{Date d1(2025, 3, 25);d1 += 100;d1.Print();return 0;
}
运行结果是正确的:
提问
operator+=的代码真的没有问题吗?
答:其实仍然有问题.
使用下面这个测试代码:
#include "Date.h"
int main()
{Date d1(2025, 3, 25);d1 += -100;d1.Print();return 0;
}
运行结果:日是负数,错误
修复
需要添加判断:加一个负数等于减这个数的相反数,需要调用operator-=,会在CD20.【C++ Dev】类和对象(11) 日期类对象的成员函数(++、--、日期-日期)文章实现
Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
代码复用
+和+=的代码有些重复,代码如下:
1.+复用+=
Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
2.+=复用+
Date& Date::operator+=(int day)
{*this = *this + day;return *this;
}
提问
对比以上两段代码,哪个写法更高效?
修改函数,打印测试信息:
Date::Date(int year , int month, int day)
{cout <<"构造函数:Date::Date(int year , int month, int day)" << endl;_year = year;_month = month;_day = day;
}Date::Date(const Date& x)
{cout << "拷贝构造函数:Date::Date(const Date& x)" << endl;_year = x._year;_month = x._month;_day = x._day;
}
测试代码(在没有优化的情况下,问一共拷贝对象几次?)
#include "Date.h"
int main()
{cout << "d1和d2初始化:" << endl;Date d1(2025, 3, 25);Date d2(1, 1, 1);cout << endl;cout << "d2=d1 + 100如下:" << endl;d2=d1 + 100;cout << endl;cout << "d1 += 100如下:" << endl;d1 += 100;return 0;
}
+复用+=调用拷贝构造函数次数如下:
可画过程图分析:
(注:Date tmp(*this)传的是*this,为Date::Date(const Date& x)需要的参数)
结果:拷贝对象一共两次
测试代码执行结果:
(VS显示只调用拷贝对象一次的原因:编译器做了优化)
如果在MSDOS下的老编译器Tubro C++上测试:
#include <iostream.h>
#include <conio.h>
class Date
{
public:int GetMonthDay(int year,int month){static int day[] = { 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 day[month];}Date (int year , int month, int day){cout << this <<"Date (int year , int month, int day)" << endl;_year = year;_month = month;_day = day;}Date (const Date& x){cout << "Date(const Date& x)" << endl;_year = x._year;_month = x._month;_day = x._day;}Date operator+(int day){Date tmp(*this);tmp += day;return tmp;}Date& operator+=(int day){_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;}int _year;int _month;int _day;
};int main()
{cout << "Initializing d1 and d2......" << endl;Date d1(2025, 3, 25);Date d2(1, 1, 1);cout << endl;cout << "d2=d1 + 100:" << endl;d2=d1 + 100;cout << endl;cout << "d1 += 100:" << endl;d1 += 100;getch();return 0;
}
(旧编译器下头文件是.h结尾的,最后的getch()是为了让运行临时暂停,好查看结果)
运行结果:
(没有优化,一共拷贝对象2次)
+=复用+调用拷贝构造函数次数如下:
可画过程图分析:
结果:拷贝对象一共四次
测试代码执行结果:
(VS显示只调用拷贝对象两次的原因:编译器做了优化)
如果在MSDOS下的老编译器Tubro C++上测试:
#include <iostream.h>
#include <conio.h>
class Date
{
public:int GetMonthDay(int year,int month){static int day[] = { 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 day[month];}Date (int year , int month, int day){cout << this <<"Date (int year , int month, int day)" << endl;_year = year;_month = month;_day = day;}Date (const Date& x){cout << "Date(const Date& x)" << endl;_year = x._year;_month = x._month;_day = x._day;}Date operator+(int day){Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year,tmp._month)){tmp._day -= GetMonthDay(tmp._year,tmp._month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;}Date& operator+=(int day){*this = *this + day;return *this;}int _year;int _month;int _day;
};int main()
{cout << "Initializing d1 and d2......" << endl;Date d1(2025, 3, 25);Date d2(1, 1, 1);cout << endl;cout << "d2=d1 + 100:" << endl;d2=d1 + 100;cout << endl;cout << "d1 += 100:" << endl;d1 += 100;getch();return 0;
}
运行结果是这样的:
(没有优化,一共拷贝对象4次)
练习
日期-日期问题:
https://leetcode.cn/problems/number-of-days-between-two-dates/
下篇博客将分析此题