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

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

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

目录

一. 接口实现说明

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;
}

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1295483.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存