类与对象(三)(日期类实现)

发布于:2024-04-25 ⋅ 阅读:(25) ⋅ 点赞:(0)

日期类实现

//Date.cpp
#include"Date.h"

Date::Date(int year, int month, int day) {
	_year = year;
	_month = month;
	_day = day;//很简单的赋值

	if (CheckInvalid()) {
		printf("日期赋值错误");
	}
}

bool Date::CheckInvalid() const {
	return _year<1 || _month < 1 || _month>12 || _day>GetMonthDay(_year, _month) || _day < 1;
}

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

Date::~Date() {
	//由于Date类中并未动态开辟任何空间,所以析构函数可以为空
	_year = _month = _day = 0;
}

Date& Date::operator=(const Date& d) {
	// 检查自赋值
	if (this != &d) {
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this; // 返回当前对象的引用,以实现链式赋值
	//返回引用则是因为更有效率,不用再在传值的时候使用拷贝构造
}

bool Date::operator<(const Date& d) const{
	if (_year < d._year) {
		return true;
	}

	else if (_year == d._year) {
		if (_month < d._month) {
			return true;
		}

		else if (_month == d._month) {
			if (_day < d._day) {
				return true;
			}
		}
	}

	return false;
}

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

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

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

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

bool Date::operator!=(const Date& d) const{
	return !(*this == d);
}

Date Date::operator+(int day) const{
	Date tmp(*this);//Date tmp=*this也是拷贝构造,因为tmp先前并不存在
	tmp += day;
	return tmp;//因为之后tmp就会销毁,所以这里我们并未返回引用,不然会导致野引用,-亦然
}

Date& Date::operator+=(int day) {
	_day += day;

	while (1) {
		if (_day > GetMonthDay(_year, _month)) {
			_day -= GetMonthDay(_year, _month);
			_month++;

			if (_month == 13) {
				_month = 1;
				_year++;
			}
		}

		else
			break;
	}

	return *this;
}

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

Date& Date::operator-=(int day) {
	_day -= day;

	while (_day <= 0) {
		_month--;

		if (_month == 0) {
			_year--;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

//++d1
Date& Date::operator++() {
	(*this) += 1;
	return *this;
}

//d1++,加入形参int便于分辨
Date Date::operator++(int) {
	Date tmp(*this);
	*this += 1;
	return tmp;//这里同理,返回的是值不是对象
}

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

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

//d1-d2,算两个日期之间的差值
int Date::operator- (const Date& d) const{
	int flag = 1;
	Date max = *this;
	Date min = d;

	if (*this < d) {
		min = *this;
		max = d;
		flag = -1;
	}

	int n = 0;
	while (min != max) {
		min++;//虽然效率低,但是准的
		n++;
	}
	//这里的min!=max也是有讲究的,!=的话效率高一些,如果用<,就会衍生很多判断
	return n * flag;//flag就是判断正负
}

//流插入操作符
ostream& operator<<(ostream& out, const Date& d) {
	out << d._year << '/' << d._month << '/' << d._day << endl;//其实这里out跟cout是一样的
	return out;
}

//流提取操作符
istream& operator>>(istream& in, Date& d) {
	while (1)
	{
		cout << "请依次输入年月日:>";
		in >> d._year >> d._month >> d._day;

		if (d.CheckInvalid())
		{
			cout << "亲,输入了无效日期,请重新输入" << endl;//这里还可以判断正误
		}
		else
		{
			break;
		}
	}

	return in;
}

为了方便,这里我们直接把日期类的实现整个搬了过来,而这是存储在Date.cpp中的,Date.h则如下所示:

#include<iostream>
#include<assert.h>

using namespace std;

class Date {
private:
	int _year;
	int _month;
	int _day;

public:
	bool CheckInvalid() const;
	Date(int year = 1, int month = 1, int day = 1);
	Date(const Date& d);
	~Date();

	Date& operator = (const Date& d);
	bool operator<(const Date& d) const;//加const的是const成员函数,适用于只读不写的成员函数
	bool operator<=(const Date& d) const;//写法就是在最后加一个const
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;

	Date operator+(int day) const;
	Date& operator+=(int day);
	Date operator-(int day) const;
	Date& operator-=(int day);

	//++d1
	Date& operator++();
	//d1++,加入形参int便于分辨
	Date operator++(int);

	Date& operator--();
	Date operator--(int);

	//d1-d2,算两个日期之间的差值
	int operator-(const Date& d) const;

	//本质上声明和定义不分离就是内联
	int GetMonthDay(int year, int month) const {
		assert(month > 0 && month < 13);
		static int MonthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//这里是为了不让每次调用函数时都需要生成MonthDays数组
		
		if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || year % 400 == 0) {
			return 29;//return 29,不改变数组,不然后续使用麻烦
			//month==2放在前面的原因是因为避免不是2月份的月份还要进行是否是闰年的判断
		}

		return MonthDays[month];
	}

	friend ostream& operator<<(ostream& out, const Date& d);//我们用友元声明,是为了访问我们的私有对象
	friend istream& operator>>(istream& in, Date& d);

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

ostream& operator<<(ostream& out, const Date& d);//在全局定义,是为了调整this指针的位置,防止其在使用时出现的d1<<cout的情况
istream& operator>>(istream& in, Date& d);

而下面我们就将把一些比较难一些的函数,以及一些需要注意的点为大家解释一下:
1.< > ==等大小比较运算符
首先我们可以先实现<和=,这样子,我们就可以直接运用这两个运算符,在其他函数中复用,而不需要写那么多代码
2.-,算两个日期之间的天数之差
由于直接用两个日期减的话,肯定会很难,进位退位一些的实现都很麻烦,所以我们使用一个比较简单的方法,只是在效率上有所欠缺。
首先,我们假设,this指向的对象为大一些的数,而另一个指向的则是小一些的,定义一个flag为1,用来确定正负;如果假设错误则让它们互换,并且flag变为-1;而后面则是定义一个while循环,然后让min++走到max,循环条件为min!=max,并且定义一个n用来储存两者之间相差的天数,最后输出n*flag即可
3.为什么先实现+=和-=,而不是+和-
这个也很好解释,如果我们先实现+=这些,那么我们的+=不会创建一个临时对象,而+会;而如果先实现+,那么不仅+需要创建临时对象,而且+=也需要,这很浪费效率
4.流插入和流提取
我们在使用中会发现,我们无法对自定义类型使用流插入和流提取符号,而这需要我们在类中自己定义
cout是ostream类中的一个对象,所以我们用out来代替cout,后面的cin同理
我们一开始想到的是直接在类中定义声明,也就是像下面这样写:
在这里插入图片描述
我们会发现只有这么写,程序才可以正常运行,而如果我们改成正常的写法呢?
在这里插入图片描述
仔细分析,我们会发现,由于隐含的this指针是排在第一个参数的位置上的,而out是第二个参数,做右操作数,所以我们只能这么写。那有什么方法可以让其变成正常的样子吗?
既然我们无法在类中定义,那么我们就可以尝试在全局中使用,但是这也有一个问题,那就是我们需要封装,所以如果定义全局函数,那么如何去调用日期类里面的成员变量呢?这里我们就需要用到friend:友元声明,可以访问我们的私有对象,需要在头文件的类里面声明,然后在全局定义,这样子才得以将我们的对象顺序调换;并且,为了使我们可以连续输出和输入,就像连续赋值一样,我们不用void作为我们的返回值,而是ostream&和istream&,才让我们可以链式操作
5.对日期类初始化的检查
虽然我们创造了日期类的构造函数,但是我们却没有在创建的时候检查我们的日期是否合法,而这让我们创建一个一个新函数来检查:bool CheckInvalid();通过将其放在构造函数之中,我们只用在一开始就检查其是否符合规则

以上就是日期类的一些疑难解答,大家也可以自己观察头文件以及定义文件,希望大家能够在文章中学到东西!下一篇文章我们继续加油!