C++入门级练手项目——日期类的计算

注:主要是练习运算符重载的使用

目录

一. 接口实现说明

1. 将要实现接口

2. 逻辑运算符重载接口

3. 算术运算符重载接口

4. << 和 >>接口

二. 全部代码展示


一. 接口实现说明

1. 将要实现接口

// 日期+=天数
     Date& operator+=(int day);

     // 日期+天数
     Date operator+(int day)const;

     // 日期-天数
     Date operator-(int day)const;

     // 日期-=天数
     Date& operator-=(int day);

     // 前置++
     Date& operator++();

     // 后置++
     Date operator++(int);

     // 后置--
     Date operator--(int);

     // 前置--
     Date& operator--();
 
     // >运算符重载
     bool operator>(const Date& d)const;

     // ==运算符重载
     bool operator==(const Date& d)const;

     // >=运算符重载
     inline bool operator >= (const Date& d)const;
 
     // <运算符重载
     bool operator < (const Date& d)const;

     // <=运算符重载
     bool operator <= (const Date& d)const;

     // !=运算符重载
     bool operator != (const Date& d)const;

     // 日期-日期 返回天数
     int operator-(const Date& d)const;

2. 逻辑运算符重载接口

小于操作符重载:

需要注意的是按顺序比,先比年,再比月,最后比天,这些是建立在前面的都相等的情况下。这里是类外实现,需要指定类域。

//<重载
bool Date::operator<(const Date& d)const
{
	if ((_year < d._year) ||
		(_year == d._year && _month < d._month) ||
		(_year == d._year && _month == d._month && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}

等于操作符重载:

类外实现,需要指定类域。

//==重载
bool Date::operator==(const Date& d)const
{
	return ((_year == d._year) && (_month == d._month) && (_day == d._day));
}

注:由于已经实现了等于和小于操作符的重载,大于、大于等于、小于等于、不等于可以复用这两个操作符,以下采用复用的方式实现并且在类域内实现,采用内联的方式实现(类型函数自带隐藏的内联关键字,会被编译器处理成内联函数)。

不等于操作符重载:

在类内实现,不用指明类域

    //!=重载
	bool operator!=(const Date& d)const
	{
		//复用等于
		return (!(*this == d));
	}

小于等于操作符重载:

在类内实现,不用指明类域。

//<=重载
	bool operator<=(const Date& d)const
	{
		//复用<和==
		return ((*this) < d || (*this) == d);
	}

大于操作符重载:

在类内实现,不用指明类域。

    //>重载
	bool operator>(const Date& d)const
	{
		//复用小于等于
		return (!(*this <= d));
	}

大于等于操作符重载:

在类内实现,不用指明类域。

    //>=重载
	bool operator>=(const Date& d)const
	{
		//复用小于
		return (!(*this < d));
	}

3. 算术运算符重载接口

赋值操作符重载:

需要注意的问题是有一种情况看似使用了赋值操作符其实使用拷贝构造函数:

    //区分赋值与拷贝构造函数的情况
	Date d1(2022, 5, 19);
	Date d2 = d1;
	//调试查看发现这行代码其实是按这行代码的方式执行的Date d2(d1);

	Date d3;
	Date d4;
	d3 = d4;//这行代码才调用了重载赋值操作符函数

以上这种情况需要自己额外去关注,调试才能看出来,如果想要验证,需要自己先写一个拷贝构造函数,在调试下去进行观察。这里是在类外实现,需要指定类域。

//=重载
Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;;
	_day = d._day;

	//传引用,减少拷贝,*this没有销毁,this销毁了
	return *this;
}

日期+=天数:

类外实现,需要指明类域。需要注意当天数为负数的情况,以及返回值可以采用引用返回。

// 日期+=天数
Date& Date::operator+=(int day)
{
	//处理天数带入负数时的情况
	if (day < 0)
	{
		*this -= -day;//注意是-day,不然会-=和+=反复运行
		return *this;
	}

	//将需要加的天数加上
	_day += day;
	//处理溢出问题
	while (_day > GetMonthDay(_year, _month))
	{
		//更新天
		_day -= GetMonthDay(_year, _month);
		//月+1
		++_month;
		//年+1
		if (_month > 12)
		{
			_month = 1;
			++_year;
		}
	}
	//传引用比传值效率高
	return *this;
}

日期-=天数:

与日期+=天数同理。

// 日期-=天数
Date& Date::operator-=(int day)
{
	//处理带入负数的情况
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	//这里与+=同理
	_day -= day;

	while (_day < 0)
	{
		--_month;
		if (_month < 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

日期-日期,需要返回天数:

这里和日期-天数的关系是函数的重载。

需要先找到日期更大的那个,然后让日期小的一直并且统计加的天数,直到与日期大的相等为止,这个天数就是两者中间所相差天数,但是需要注意可能会是小日期减大日期的情况,所以引入flag来控制最后返回正负。

// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
	//找到谁大谁小
	Date max = *this;
	Date min = d;
	//需要返回负或正的天数
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	//计算max和min中间的天数
	int num = 0;
	while (max != min)
	{
		++min;
		++num;
	}

	return flag * num;

}

注:由于已经实现了+=和-=操作符的重载,加、减、前置加加、后置加加、前置减减、后置减减可以复用这两个操作符,以下采用复用的方式实现并且在类域内实现,采用内联的方式实现(类型函数自带隐藏的内联关键字,会被编译器处理成内联函数)。

以下前置和后置加加(减减)需要注意:为了区分前置和后置加加(减减),后置加加(减减)的参数需要多一个int类型的参数(只能是int类型,我们不能更改,编译器决定的),这个参数没有实际意义,只是为了区分,一般都只加int,不加上形参,表示不接收值或接收不使用传过来的值,别想着使用全省参数这种操作,会报错的,

日期+天数:

这里需要注意,我们采用+复用+=仅仅是为了提高效率,+=复用+是可以的!

// 日期+天数
	Date operator+(int day)const
	{
		//这里使用+复用+=而不是+=复用+的原因是+要进行两次拷贝,而+=不用拷贝
		//同时调用两个函数时,只用拷贝两次而不是四次,效率更高

		//本身不能改
		Date ret = *this;

		ret += day;//复用+=

		return ret;//ret会销毁,只能传值
	}

日期-天数:

这里-复用-=原因和上面+复用+=的原因是一样的

    // 日期-天数
	Date operator-(int day)const
	{
		Date ret = *this;

		ret -= day;//复用-=

		return ret;
	}

++日期:

    // 前置++
	Date& operator++()
	{
		*this += 1;//复用+=

		return *this;
	}

日期++:

    // 后置++
	Date operator++(int)
	{
		Date ret = *this;

		*this += 1;//复用+=

		return ret;
	}

--日期:

    // 前置--
	Date& operator--()
	{
		*this -= 1;//复用-=

		return *this;
	}

日期--:

    // 后置--
	Date operator--(int)
	{
		Date ret = *this;

		*this -= 1;//复用-=

		return ret;
	}

4. << 和 >>接口

<<接口:

//cout重载
std::ostream& operator<<(std::ostream& _out, const Date& d)
{
    /*这里获取每月的天数遇到了一个问题,去取对象里的获取每月天数函数时,由于我们的对象是只读的,但是一开始我们GetMonthDay设置的是可读可写的,这里对象去调用获取每月天数时,就出现了权限的放大,由于GetMonthDay还调用了IsLeapyear函数,所以IsLeapyear函数也要是只读的,不能可读可写*/
    if(d._year > 0 && d._month > 0 && d._month < 13 && d._day > 0 && d._day <= d.GetMonthDay(d._year, d._month))
    {
         _out << d._year << "-" << d._month << "-" << d._day << endl;
    }
    else
    {
        _out << "illegal input!"<< endl;
    }
    return _out;
}

>>接口:

//cin重载
std::istream& operator>>(std::istream& _in, Date& d)
{
    _in >> d._year >> d._month >> d._day;
    return _in;
}

二. 全部代码展示

Date.h文件中的代码

#pragma once
#include 

using std::cout;
using std::cin;
using std::endl;

class Date
{
    friend std::ostream& operator<<(std::ostream& _out, const Date& d);
    friend std::istream& operator>>(std::istream& _in, Date& d);
public:
	//创建默认构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		//非法输入处理
		if (year > 0 && month > 0 && month < 13 && day > 0 && day <= GetMonthDay(year, month))
		{
			_year = year;
			_month = month;
			_day = day;
		}
		else
		{
			cout << "输入有误,请重新输入!" << endl;
		}
	}
	//析构函数可以不用创建,拷贝构造函数也可以不用创建
	//为了方便查看特性,这里会完成拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	//取每月天数
	int GetMonthDay(const int& year, const int& month)const;

	//返回是否是闰年
	bool Isleapyear(const int& year)const;



	//==重载
	bool operator==(const Date& d)const;

	//=重载
	Date& operator=(const Date& d);

	//<重载
	bool operator<(const Date& d)const;

	// 日期+=天数
	Date& operator+=(int day);

	// 日期-=天数
	Date& operator-=(int day);

	// 日期-日期 返回天数
	int operator-(const Date& d)const;



	//以下复用了函数的函数采用内联的函数
	//!=重载
	bool operator!=(const Date& d)const
	{
		//复用等于
		return (!(*this == d));
	}

	//<=重载
	bool operator<=(const Date& d)const
	{
		//复用<和==
		return ((*this) < d || (*this) == d);
	}

	//>重载
	bool operator>(const Date& d)const
	{
		//复用小于等于
		return (!(*this <= d));
	}

	//>=重载
	bool operator>=(const Date& d)const
	{
		//复用小于
		return (!(*this < d));
	}




	// 日期+天数
	Date operator+(int day)const
	{
		//这里使用+复用+=而不是+=复用+的原因是+要进行两次拷贝,而+=不用拷贝
		//同时调用两个函数时,只用拷贝两次而不是四次,效率更高

		//本身不能改
		Date ret = *this;

		ret += day;//复用+=

		return ret;//ret会销毁,只能传值
	}

	// 日期-天数
	Date operator-(int day)const
	{
		Date ret = *this;

		ret -= day;//复用-=

		return ret;
	}
	


	// 前置++
	Date& operator++()
	{
		*this += 1;//复用+=

		return *this;
	}

	// 后置++
	Date operator++(int)
	{
		Date ret = *this;

		*this += 1;//复用+=

		return ret;
	}

	// 前置--
	Date& operator--()
	{
		*this -= 1;//复用-=

		return *this;
	}

	// 后置--
	Date operator--(int)
	{
		Date ret = *this;

		*this -= 1;//复用-=

		return ret;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

Date.cpp文件中的代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include "Date.h"

bool Date::Isleapyear(const int& year)const 
{
	//四年一润,百年不润,四百年润
	return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}

int Date::GetMonthDay(const int& year, const int& month)const
{
	//对应下标访问月份天数,使用static就不要一直创建数组,可以提高效率,防止多线程安全则加const
    const static int MonthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 30 };

	//二月份闰年处理
	if (month == 2 && Isleapyear(year))
	{
		return 29;
	}
	else
	{
		return MonthDayArray[month];
	}
}




//=重载
Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;;
	_day = d._day;

	//传引用,减少拷贝
	return *this;
}

//==重载
bool Date::operator==(const Date& d)const
{
	return ((_year == d._year) && (_month == d._month) && (_day == d._day));
}

//<重载
bool Date::operator<(const Date& d)const
{
	if ((_year < d._year) ||
		(_year == d._year && _month < d._month) ||
		(_year == d._year && _month == d._month && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}



// 日期+=天数
Date& Date::operator+=(int day)
{
	//处理天数带入负数时的情况
	if (day < 0)
	{
		*this -= -day;//注意是-day,不然会-=和+=反复运行
		return *this;
	}

	//将需要加的天数加上
	_day += day;
	//处理溢出问题
	while (_day > GetMonthDay(_year, _month))
	{
		//更新天
		_day -= GetMonthDay(_year, _month);
		//月+1
		++_month;
		//年+1
		if (_month > 12)
		{
			_month = 1;
			++_year;
		}
	}
	//传引用比传值效率高
	return *this;
}

// 日期-=天数
Date& Date::operator-=(int day)
{
	//处理带入负数的情况
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	//这里与+=同理
	_day -= day;

	while (_day < 0)
	{
		--_month;
		if (_month < 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
	//找到谁大谁小
	Date max = *this;
	Date min = d;
	//需要返回负或正的天数
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	//计算max和min中间的天数
	int num = 0;
	while (max != min)
	{
		++min;
		++num;
	}

	return flag * num;
}

//cout重载
std::ostream& operator<<(std::ostream& _out, const Date& d)
{
    /*这里获取每月的天数遇到了一个问题,去取对象里的获取每月天数函数时,由于我们的对象是只读的,但是一开始我们GetMonthDay设置的是可读可写的,这里对象去调用获取每月天数时,就出现了权限的放大,由于GetMonthDay还调用了IsLeapyear函数,所以IsLeapyear函数也要是只读的,不能可读可写*/
    if(d._year > 0 && d._month > 0 && d._month < 13 && d._day > 0 && d._day <= d.GetMonthDay(d._year, d._month))
    {
         _out << d._year << "-" << d._month << "-" << d._day << endl;
    }
    else
    {
        _out << "illegal input!"<< endl;
    }
    return _out;
}

//cin重载
std::istream& operator>>(std::istream& _in, Date& d)
{
    _in >> d._year >> d._month >> d._day;
    return _in;
}

详情已在代码注释说明,参考代码理解。

你可能感兴趣的:(#,C++,c++)