c++ 日期类的实现(类和对象学习)

1. 基础内容

这部分在上篇文章中有详细介绍,先实现前面的基础部分

1. 成员变量

日期类我们需要有成员变量,为了后面写函数容易区分变量,我们在成员变量前加上 “_” 。

class Dare
{
private:
	int _year;
	int _month;
	int _day;
};

2. 日期类的构造函数

Date(int year =1,int month =1, int day =1)
{
	_year=year;
	_month=month;
	_day=day;
};

由于日期类不涉及内存开辟的问题,所以我们这里不需要写析构函数,使用默认的即可。

3. 日期的运算符重载

先实现 日期之间的比较

bool operator<(const Date& d)   //小于
{
   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 operator==(const Date& d)
{
   return _year == d._year && _month == d._month && _day == d._day;
}

我们先实现 小于 和 等于 这两个操作
对于不等于,大于,大于等于,小于等于,我们通过复用上面的函数即可

bool operator<=(const Date& d)
{
	return *this < d || *this == d;
}
bool opeerator>(const Date& d)
{
	return !( *this <= d );
}
bool operator>=(const Date& d)
{
	return !( *this < d );
}
bool operator!=(const Date& d)
{
	return !( *this == d );
}

4. 判断当前月的天数

int GetMonthDay(int year, int month)
{
	const static int MonthArray[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
	//使用const防止修改,static多次调用更方便
	//数组定义13,下标用月数即可,不用month-1
	if(month==2 && (year%4 == 0 && year%100 != 0 || year%400 == 0))
	//这里最好先检查month是不是等于2,如果最后检查month==2,效率会慢一点
	{
		return 29;
	}
	return MonthArray[month];
}

5. 输出

void Print()
{    
    cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
}

上面就是之前学的基础部分
下面会对日期类的功能进行完善


2. 功能完善

1. 初始化问题

上面我们写的构造函数,可以完成初始化,但是仅仅是做了赋值操作。
如果出现下面的情况
2023 / 2 / 29
2022 / 13 / 30
2000 / 11 / 90
上面的情况都是非法的日期,如果这种数据进入程序,很可能会出现bug,所以要限制一下。
比如出现非法日期提醒一下

Date(int year =1,int month =1, int day =1)
{
	_year = year;
	_month = month;
	_day = day;
	if(month < 1 || month > 12 || day < 1 || day > GetMonthDay( year , month))
	//这里一定要先检查month,如果先进行GetMonthDay(),可能会造成越界访问
	{
		cout<<"非法日期:"<<;
		Print();
	}
}

这样的话,如果我们输入的数据有问题,就会提示我们在这里插入图片描述

2. 赋值重载

赋值运算符的重载----复制拷贝

void operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

和拷贝构造函数不同

void operator=(const Date& b);

Date(const Date& d);
  1. 拷贝函数如果不加引用,是为了防止无穷递归,防止每一次传入参数的 Date d 都被识别为拷贝。
    而赋值重载可以不加,可以理解为传入的是新创建的局部变量。
    • 赋值和拷贝的区别
      赋值:两个已经存在的对象进行拷贝。
      拷贝构造:一个已经存在的对象,去初始化另一个对象。
  2. 这里的赋值重载,可以使用 Date b 进行传参,但是每次传入都会进行一次拷贝,效率会慢一点,所以如果内部没有对传入参数进行修改,可以使用传入引用
void operator=(const Date& b);

这样就不需要拷贝,而且因为加入 const 进行修饰,我们也不能在函数内修改传入参数。
3. 如果连续赋值

int main()
{
	Date a(2023, 2, 19);
	Date b(2022, 3, 30);
	Date c(2023, 11, 20);
	a.Print();
	b.Print();
	c.Print();
	a = b = c;
	a.Print();
	b.Print();
	c.Print();
	return 0;
}
  1. 自己给自己赋值
int main()
{
    Date d1;
    d1=d1;
    return 0;
}

虽然上面的写法语法上没有问题,但是没有任何意义,编译器还是会根据代码,自动执行,对没意义的事操作,只会浪费时间,所以我们可以改进一下代码。

Date& operator=(const Date& d)
{
    if(this != &d)//判断不能自己给自己赋值,相同的赋值没意义,直接返回
    {
        _year=d._year;
        _month=d._month;
        _day=d._day;
    }
    return *this;
}
  1. 赋值重载的特点
  • 对内置类型来说有用,浅拷贝的足够使用
  • 自定义类型会自动调用对应的赋值重载函数

3. 运算符重载的使用价值

也是日期类最主要的部分,对运算符重载的使用

1. 日期+=天数

这里使用 += ,会直接修改原来的值,所以函数内直接对原来的值进行操作。
先将天数直接加在原天数上,如果相加后的结果大于当前月的天数,天数减去当前月的天数,月数++,月数为13的话,年数++,月数从1开始,直到天数小于当前月的天数

Date& operator+=(int day)
{
    _day += day;
    while(_day > GetMonthDay(_year, _month))
    {
        _day -= GetMonthDay(_year, _month);
        _month++;
        if (_month == 13)
        {
            _year++;
            _month = 1;
        }
    }
    return *this;
}

在这里插入图片描述

2. 天数+日期

Date Date::operator+(int day)
{
    Date tmp = *this;
    tmp += day;
    return tmp;
}

在这里插入图片描述

有了 += ,我们直接复用 += ,创建一个 tmp 进行+=,然后返回 tmp ,这样原来的参数不会改变,也可以完成 + 的操作。

3. += 复用 +

上面实现了 + 复用+= ,如果我们用 + 实现 +=,会不会效率更高?
先实现 +

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 (tmp._month == 13)
        {
            tmp._year++;
            tmp._month = 1;
        }
    }
    return tmp;
}

和上面逻辑一样,只不过这里是对赋值后的tmp进行操作,能保证原参数不会改变。

Date& Date::operator+=(int day)
{
Date& Date::operator+=(int day)
{
    *this = *this + day;
    return *this;
}

在这里插入图片描述
使用方面没问题,我们对比一下,两种复用的实现方式,有什么区别。
在这里插入图片描述
左边 构造 tmp 拷贝一次
加等拷贝一次
右边 构造 tmp 拷贝一次
加等拷贝一次
在这里插入图片描述
左边没有拷贝,只涉及运算修改原数据
右边 *this+day 调用上面的的,两次拷贝
*this=*this+day 赋值

  • 相比之下,上面 +复用+= 更好,因为实现 += 没有复用其他函数,直接通过运算得到的,效率上更快。

4. 日期 -=

Date& Date::operator-=(int day)
{
    _day -= day;
    while (_day <= 0)
    {
        _day += GetMonthDay(_year, _month);
        _month--;
        if (_month == 0)
        {
            _year--;
            _month = 12;
        }
    }
    return *this;
}

和上面 += 的实现逻辑类型。

5. 日期 - 天数

Date Date::operator-(int day)
{
    Date tmp = *this;
    tmp -= day;
    return tmp;
} 

直接复用,很香。

6. +=/-=负数

对于上面实现的 += ,-= ,如果天数是负数,我们的程序就会出现问题

Date Date::operator-(int day)
{
    Date tmp = *this;
    tmp -= day;
    return tmp;
} 

在这里插入图片描述
所以就要进行优化,如果 += 传入负数,可以理解为 -= 正数,-= 传入负数,可以理解为 += 正数
所以我们只要判断如果传入的是负数,直接复用另一个即可

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;
}
Date& Date::operator-=(int day)
{
    if (day < 0)
    {
        *this += (-day);
        return *this;
    }
    _day -= day;
    while (_day <= 0)
    {
        _day += GetMonthDay(_year, _month);
        _month--;
        if (_month == 0)
        {
            _year--;
            _month = 12;
        }
    }
    return *this;
}

7. 前置++,后置++

函数名都是++ ,参数也都只有一个参数,可以认为只有返回值不同。
后置 返回++之前的
前置 返回++之后的

Date operator++();

前置和后置++ 函数名一样,为了做区分,我们把上面这种样子作为前置++,后置++需要我们传入int 类型的参数用来区分

//前置:
Date operator++();
++d1->d1.operator++()
//后置:
Date operator++(int);
d1++ -> operator++(0)//只要传入即可,传入什么都可以,只是进行类型判断,不使用接收值
//后置++的传入参数是为了区分构成函数重载

后置++的参数只是为了调用函数的时候有个区分,函数内参数是没有意义的。
上面的运算符重载也构成函数重载
只有自定义类型可以运算符重载
int 的参数只是为了区分前置和后置++函数的区别

Date Date::operator++()
{
    *this += 1;
    return *this;
}
//前置没有拷贝,前置平时使用好一点
Date Date::operator++(int)
{
    Date tmp(*this);
    *this + 1;
    return tmp;
}

使用的话直接复用上面的即可。
在这里插入图片描述

8. 日期差

如果有两个日期,想要计算两个日期之间差了多少天数。
思路:先判断那个天数大,用小的日期追大的日期,每次++,计数的变量++,直到小的追上大的

int Date::operator-(const Date& d)
{
    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;
}

9. 代码

Date.h

#pragma once
#include<iostream>
using namespace std;

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1);//初始化
    void Print()const;//输出年份
    int GetMonthDay(int year, int month);//     当前月有几天
    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);//不等于     !=
    Date& operator+=(int day);//加等            +=
    Date& operator=(const Date& d);//赋值       =
    Date operator+(int day);//加                +
    Date& operator-=(int day);//减等            -=
    Date operator-(int day);//减                -
    Date operator++();//前置++                  前置++
    Date operator++(int);//后置++               后置++
    Date operator--();//前置--                  前置--
    Date operator--(int);//后置--               后置--
    int operator-(const Date& d);//日期减日期   日期差
private:
    int _year;
    int _month;
    int _day;
};

Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
    _year = year;
    _month = month;
    _day = day;

    if (month < 1 || month>12 || day<1 || day>GetMonthDay(year, month))
    {
        cout << "非法日期" << ":";
    }
}

void Date::Print()const
{
    cout << _year << "/" << _month << "/"<<_day << endl;
}

int Date::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];
}
bool Date::operator<(const Date& d)
{
    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)
{
    return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d)
{
    return *this < d || *this == d;
}
bool Date::operator>(const Date& d)
{
    return  !(*this <= d);
}
bool Date::operator>=(const Date& d)
{
    return  !(*this < d);
}
bool Date::operator!=(const Date& d)
{
    return !(*this == d);
}
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;
}
Date& Date::operator=(const Date& d)
{
    if (this != &d)//判断不能自己给自己赋值,相同的赋值没意义,直接返回
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}

Date Date::operator+(int day)
{
    Date tmp = *this;
    tmp += day;
    return tmp;
}

Date& Date::operator-=(int day)
{
    if (day < 0)
    {
        *this += (-day);
        return *this;
    }
    _day -= day;
    while (_day <= 0)
    {
        _day += GetMonthDay(_year, _month);
        _month--;
        if (_month == 0)
        {
            _year--;
            _month = 12;
        }
    }
    return *this;
}

Date Date::operator-(int day)
{
    Date tmp = *this;
    tmp -= day;
    return tmp;
}

Date Date::operator++()
{
    *this += 1;
    return *this;
}
Date Date::operator++(int)
{
    Date tmp(*this);
    *this += 1;
    return tmp;
}

Date Date::operator--()
{
    *this -= 1;
    return *this;
}
Date Date::operator--(int)
{
    Date ret = *this;
    *this -= 1;
    return ret;
}

int Date::operator-(const Date& d)
{
    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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值