C++之初识“类” (2):增添“日期”MyDate类的功能

前情提要

#上期传送门#
C++之初识“类”:写一个关于日期的类(附两种日期间隔计算)
https://blog.csdn.net/henry_23/article/details/104832525

前言

第二次课,讲了构造函数,作业要求是在第一次的基础上增添一些功能:

MyDate类增加功能,要求如下:
(1) 为类增加有参无参的构造函数,初始化为"合法"的日期(比如:2020年2月30日就不合法)
(2) 增加一个成员函数:MyDate yesterday(),将当前对象日期设为昨天,并返回"昨天"的对象(MyDate yesterday()——这里的MyDate是返回值的类型)
(3) 完善set()函数,设置合法的日期
(4) 求一个日期在指定天数之前/后的日期
(5) 计算两个日期相距的天数
(6) 在main函数中测试新增功能

原来我上周竟猜中了下周的作业……

正文

判断日期有效性:isLegal()

首先,为了判断日期有效性,我准备写一个函数isLegal()

因为发现它和tomorrow()函数都需要获得某月的总天数,便决定先写一个days()函数,用于返回当前月份的天数

int MyDate::days()
{
    
    
    int days[13] ={
    
    0,31,28,31,30,31,30,31,31,30,31,30,31};
    if (month==2)
    {
    
    
        if(isLeap()) return 29;
        else return 28;
    }
    else return days[month];
}

下面来写isLegal(),这里,我觉得年月日三者的检验需要有顺序。因为判断天数时需要调用days()来判断,而days()要求月份是合法的,所以调用days()之前需要先校验月份。从另一个角度来说,不确定年份、月份,何谈日子是否正确?

  1. 先判断年份是否合法;
  2. 再判断月份是否大于12或者小于1;
  3. 再判断日子是否合法,是小于1还是大于了当前月份的天数;

代码如下:

int MyDate::isLegal()
{
    
    
    if (year<1) return 0;
    if (month<1||month>12) return 0;
    if (day<1||day>days()) return 0;
    return 1;
}

如果有兴趣,还可以在合法性判断的同时加入自动纠正,比如2月30日自动纠正成2月29日(或者2月28日)、1月-2日纠正为1月2日;要实现也不难,可以令isLegal()可以返回不同的值,说明不同的出错情况,然后进行相应的修改:

扫描二维码关注公众号,回复: 12493950 查看本文章

这样,我们就可以在set()或者构造函数里调用isLegal()函数进行判断,如果设定的日期不合法,就执行一些操作,比如把他改成某个日期(如“1970年1月1日”),并且在控制台输出一些提示语:

if (isLegal() == 0) 
{
    
    
    cout<<"Illegal date! Date has been set to "; //提示语句
    year = 1970; month = 1; day = 1; //执行操作
    print();
    cout<<endl;
}

# 后期备注:感谢一位同学在课堂上向老师提出的问题,这里通用一点的做法貌似应该是令set()函数返回一些值,然后在调用处使用一个flag之类的变量接受这个值,然后执行相应的操作,比如:

int MyDate::set(int y, int m, int d)
{
    
    
	MyDate backup(year,month,day); //为现有的数据创建一个备份
	year = y; month = m; day = d;
	if (isLegal() == 0) 
	{
    
    
		set(backup);
		return 0;
	}
	return 1;
}
int main()
{
    
    
	MyDate d1(2020,1,1);
	int flag = d1.set(2020,2,30);
	if (flag == 0) cout<<" d1 set failed!"<<endl;
	else cout<<"d1 set successfully!"<<endl;
	return 0;
}

最后,把合法性判断的过程加入到set(int, int, int)和构造函数里面就好了。

因为考虑到我们现有的类一定是个合法日期(即已经确保了所有set()、构造函数之类的“入口”不会发生错误),那么便不必在另一个参数为对MyDate类的引用的 set()的重载中加入合法性判断了。(当然,也没必要给无参的构造函数加入这个过程,只要保证自己设的default值无误就可以了)

构造函数

MyDate::MyDate(int y_, int m_, int d_)
{
    
    
    year = y_; month = m_; day = d_;
    if (isLegal() == 0) 
    {
    
    
        cout<<"[Construction Failure] ";
        print();
        cout<<" is illegal! "<<endl;
        cout<<"Initialization failed. Date is set to ";
        year = 1970; month = 1; day = 1;
        print();
        cout<<endl;
    }
}

set 函数:

值得说明的是,由于我们不能确保传入的y_m_d_三个参数是否为合法的组合,并且希望当它们不合法时,原来的数据不会被更改,故事先生成一个名为backup的类,为原来的数据做个备份。

void MyDate::set(int y_, int m_, int d_)
{
    
    
    MyDate backup(year,month,day);
    year = y_; month = m_; day = d_;
    if (isLegal() == 0) 
    {
    
    
        cout<<"[Set Failure] ";
        print();
        cout<<" is illegal! Date is unchanged!";
        cout<<endl;
        set(backup);
    }
}

你可能会想,为什么不把isLegal()写成带有三个参数的形式,直接判断三个参数的组合是否合法?
嗯……其实那样也是挺不错的,但是那样可能需要重新写一行语句或者一个print()的重载用来输出这个不合法的日期;而我恰好比较懒,所以就觉得不如先默认参数是正确的,直接赋值,再进行判断和其他操作,似乎会方便一些。
当然这仅仅代表我个人的想法……

计算日期间隔的函数:gap(),get_gap()

上一期里介绍了两种计算日期间隔的方法,分别为面向对象的和面向过程的,这里不再讲述。有需要的同学戳链接访问上期文档。
C++之初识“类”:写一个关于日期的类(附两种日期间隔计算)

# 后期补充:日期间隔算法的一点点小优化:C++之初识“类” (3):自己写的MyDate类的一些改进

昨日重现:yesterday()

接下来我们来写yesterday()函数,整体思路和tomorrow()类似。tomorrow()采用“进位”的思想,yesterday()采用“退位”的思想,都能很方便的实现我们想要的功能。

tomorrow()的思路已经在上期中解释了。而yesterday()函数中要注意的,就是它里面更改day的语句要放在对月份的更新后——月份还没有定下来,自然也就不能确定天数。

MyDate MyDate::yesterday()
{
    
    
    day--;
    if (day<1) 
    {
    
    
        month--;
        if(month<1)
        {
    
    
            year--;
            month = 12;
        }
        day = days();
    }
    MyDate t(year,month,day); 
    return t;
}

至于为什么老师要求返回一个MyDate类,我一时没想到什么实际的意义,就暂且当作是为了练习而写的吧。

前进 / 回滚指定天数:rolling()

有了前面的tomorrow()yesterday()的基础,实现前进 / 倒退指定天数,只需要使用循环,多次调用相应的函数即可,而无需再写复杂的语句(尤其是倒退指定天数,如果用计算的方法来写可能会很复杂)。

rolling()有两个参数,一个是整型的dif,用来传入需要前进 / 回滚的天数;另一个是默认值为0的整型参数mode,这是由于老师的要求“显示指定天数之前/之后的日期”,没有说明是否需要改变原函数,所以设置了它,用来限定是否修改原日期(默认是不修改的);

函数实现如下:

void MyDate::rolling(int dif, int mode = 0)
{
    
    
    // 0 for not modify original date;
    // 1 for silently rolling the date;
    int k = dif;
    if (mode == 0) 
    {
    
    
        MyDate tmp(year, month, day);
        if (k < 0) 
        {
    
    
            while (k != 0) 
            {
    
    
                tmp.yesterday();
                k++;
            }
            print();
            cout<<" 之前 "<<-dif<<" 天是 ";
            tmp.print();
        }
        else 
        {
    
    
            while (k != 0) 
            {
    
    
                tmp.tomorrow();
                k--;
            }
            print();
            cout<<" 之后 "<<dif<<" 天是 ";
            tmp.print();
        }
        cout<<endl;
    }
    else
    {
    
    
        if (k<0) while(k!=0) {
    
    yesterday(); k++;}
        else while (k!=0) {
    
    tomorrow(); k--;}
    }
}

# 后期补充:同样,这里有关于此算法的改进:C++之初识“类” (3):自己写的MyDate类的一些改进

展示新功能:main()

void print_gap(MyDate& A, MyDate& B)
{
    
    
    A.print(); cout<<" 和 "; B.print();
    cout<<" 间隔 ";
    cout<<A.get_gap(B);
    cout<<" 天 \n";
}
int main()
{
    
    
    MyDate day1(2020,3,16);
    MyDate day2(2020,2,30);
    cout<<endl;
    day2.set(2001,2,29);
    cout<<endl;
    day2.print();
    cout<<endl<<endl;

    print_gap(day1,day2);
    cout<<day1.gap(day2);
    cout<<endl<<endl;

    day1.rolling(3);
    day2.rolling(-2);
    return 0;
}

输出结果

[Construction Failure] 2020年2月30日 is illegal! Initialization failed.
Date is set to 1970年1月1日

[Set Failure] 2001年2月29日 is illegal! Date is unchanged!

1970年1月1日

2020年3月16日 和 1970年1月1日 间隔 18337 天
18337

2020年3月16日 之后 3 天是 2020年3月19日
1970年1月1日 之前 2 天是 1969年12月30日

结尾的话

觉得输出“x年x月x日”不够酷?试着给print加一个参数,让它能够输出你想要的风格,比如2020.3.162020/3/16Mar 16, 202016/3/2020等等;但是,你可能会发现,很多地方都使用了print()函数,加一个参数的话,要改很多很多地方,这可怎么办?能不能只修改print()函数?

嗯……我这次做作业前……又瞟了一眼书,看到了静态成员变量相关的内容,目前想到可以用它来轻松的实现风格的整体更改。

甚至还可以写一个求某日期是星期几的函数。这个,你会怎么做呢?

根据现有的函数,我想到的是设定一个基准日期,计算与之的日期间隔,然后结果对7求余,同时判断日期是在基准日之前还是之后,继而得出该日期是星期几。

最后再看一眼咱们的MyDate类吧,不知不觉都写了这么多功能了呀:

class MyDate 
{
    
    
private:
    int  year, month, day;
public:
    void set(int y_, int m_, int d_);
    void set(const MyDate& );
    int  days();
    void print();
    int  equals(const MyDate& );
    void tomorrow();
    int  isLeap();
    int  isLeap(int );
    int  isLegal();
    int  compare(const MyDate& );
    int  get_gap(const MyDate& );
    int  gap(const MyDate& );
    void rolling(int , int );
    MyDate yesterday();

    MyDate();
    MyDate(int y_, int m_, int d_);
};

猜你喜欢

转载自blog.csdn.net/henry_23/article/details/104906562