【C++】类和对象练习——日期类的实现

news2024/12/24 4:05:20

文章目录

    • 前言
    • 1. 日期的合法性判断
    • 2. 日期+天数(+/+=)
      • 2.1 +和+=的重载
      • 2.2 对于两者复用的讨论
    • 3. 前置++和后置++重载
    • 4. 日期-天数(-/-=)
    • 5. 前置- -和后置- -的重载
    • 6. 日期-日期
    • 7. 流插入<<重载
    • 8. 流提取>>重载
    • 9. 总结
    • 10. 源码展示

前言

在上一篇文章我们学习类和对象的过程中,我们不是写了一个日期类嘛。
但是我们之前实现的日期类并不是很完整,我们只是借助它来帮大家学习类和对象的知识。
那这篇文章呢,我们就在之前的基础上,再增添一些功能,实现一个比较完整的日期类,作为一个练习,来帮助我们更好的理解我们之前学过的知识。

这是我们之前一起写的不太完整的日期类:

class Date
{
public:
	//构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	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;
		return false;
	}
	bool operator<=(const Date& d)
	{
		return *this == d || *this < d;
	}
	bool operator>(const Date& d)
	{
		return !(*this <= d);
	}
	bool operator>=(const Date& d)
	{
		return !(*this < d);
	}
	bool operator!=(const Date& d)
	{
		return !(*this == d);
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	//赋值重载(对于Date类用默认生成的就行)
	//d1=d2(this就是d1,d就是d2)
	/*Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}*/
private:
	int _year;
	int _month;
	int _day;
};

那这些实现过的函数我们就不再一一讲解了,大家不熟悉的话可以回顾一下上一篇文章。
另外呢,我们最终实现的是一个完整的日期类,那方便对代码进行维护和管理,以及对实现好的日期类进行测试,我们还是像之前写数据结构一样,放在多个文件中。

1. 日期的合法性判断

我们之前呢已经给该日期类写好了构造函数:

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

并且还指定了缺省参数,那这样的话在实例化一个对象时我们就可以自己给对象指定初值,我们输入的日期是啥,该对象就会被初始化为对应的日期。

那现在有一个问题,如果我们实例化对象时给的日期不合法呢?比如:

void Datetest1()
{
	Date d1(2023, 2, 30);
	d1.Print();
}

在这里插入图片描述

不合法是不是也判断不出来啊。
那我们就对原来的构造函数做一些补充好吧,让它在给对象初始化的时候可以去判断一下对应的日期合不合法。

那要怎么判断呢?

给我们一个年月日,要判断是否合法,是不是要判断月在不在【1,12】之内以及天数有没有超过当前月的总天数啊。
但是某个月的天数是不是不好确定啊,不同月的天数不一样,而且要考虑平闰年。

那我们先来写一个获取某年某月天数的函数:

int Date::GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	int MonthArr[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;
	}
	else
	{
		return MonthArr[month];
	}
}

不过多解释了,相信大家都能看懂。

那有了这个函数,我们就可以在构造函数中去判断了:

Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13 
		&& day>0 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}
}

如果合法了,我们再去赋值,不合法就还全搞成1,要不然就是随机值了。

那这时我们再用非法日期去初始化对象:
在这里插入图片描述

这样输入的日期不合法就提示了。

2. 日期+天数(+/+=)

我们说日期+日期是不是没啥意义啊,但是日期+一个天数是不是还有点意义,可以理解成这么多天之后是几几年几月几日。

2.1 +和+=的重载

所以接下来,我们要实现一个功能就是计算一个日期加了一个天数之后得到的日期:

那具体实现的思路呢可以这样搞:
首先我们想让自定义类型Date的对象直接和整型相加,这里肯定要对+进行运算符重载。
我们拿到一个日期一个天数之后,先把天数加到日上,然后,判断此时的日是否超过了当前月的总天数(获取月份天数的函数我们之前已经实现过了),超过的话,就减去当前月的天数,然后月份++,那月份++有可能会超过12啊,一旦超过,就让年++,然后月置成1。
那当然这肯定是一个循环,当我们的日(day)不再大于当前月的天数,则此时得到的就是最终的日期。

我们来写一下代码:

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

这里*this出了作用域还在,所以可以返回引用。

那我们来测试一下:

在这里插入图片描述
在这里插入图片描述
我们看这个计算出来的日期确实是没问题的,但是d1+100,这里是+而不是+=,所以d1是不是不应该变啊,我们只是把d1+100得到的日期赋给了d2,但是现在d1也变了。
所以我们这样写其实实现的是啥,是不是+=啊。

所以我们这里重载的应该是+=:

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

那+=还需要有返回值吗,🆗,最好还是带返回值,带返回值的话就可以支持这种情况:d2 = d1 += 100;

那刚才实现的是+=,那+要这么搞?

是不是借助一个临时对象就可以搞定了啊,我们只要不改变*this就行了嘛。

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

那就这样,测试一下:
在这里插入图片描述

🆗,这次d1没有被改变。
在这里插入图片描述
答案也没问题。

那对于+的重载:

大家有没有注意到我们没有返回引用,为什么?
因为我们返回的是啥,是不是tmp,而tmp是一个局部的对象,出了作用域就销毁了,所以我们不能返回引用。
那有时候呢,有的人为了这个地方能够返回引用,对tmp加了一个static修饰,然后就返回引用。
在这里插入图片描述

大家思考一下这样可以吗?

我们试一下会发现:
在这里插入图片描述
第一次没问题,但我们第二次在调用+,是不是就错了啊。
在这里插入图片描述
第二次我们还是给d1+1000天,但结果却是2000天之后的。

为什么会这样?

原因就在于我们用了static。
我们创建的局部对象tmp被static修饰后,就不存在栈区了,而是放在静态区了,所以静态局部变量出作用域不会被销毁,而是保留在静态区,因此我们确实可以返回引用了。
但是,静态局部变量的初值是在编译期间就指定的,所以运行期间,不管我们再去调用几次函数,tmp都不会被重新初始化,而是保留上次的值,所以我们第二次的结果才会变成2000天之后。

之前有篇文章详解static的作用,大家可以看——链接: link
所以呢,这个地方我们不要为了能够返回引用而去随便加static。

2.2 对于两者复用的讨论

那除此之外:

大家看,+的重载与+=的重载,除了多一个临时的局部对象,其它的逻辑是不是一样啊,所以+里面是不是可以复用+=啊。

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

这样是不是就行了啊:
在这里插入图片描述

那既然+可以复用+=,我们是不是也可以考虑先实现+,然后+=复用+呢?

当然可以。

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

那这是我们自己写的+,现在我们+=来复用+。

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

这样不就行了。

那再来思考:

到底是+复用+=好呢,还是+=复用+好呢?
🆗,那这里呢,其实是我们的第一种即+复用+=更好一点。
因为+里面创建临时对象有一次拷贝,返回的是值而不是引用又是一次拷贝。
那如果是+复用+=的话就只有+里有拷贝,但如果是+=复用+的话,是不是+=和+里都有拷贝了。

3. 前置++和后置++重载

刚重载了+和+=,那是不是还有前置++和后置++啊,那我们也来实现一下。

先来前置++吧:

来回忆一下前置++的特性是什么?
是不是先++,后使用啊,即返回的是++之后的值。

举个例子:

int a=5;
int b=++a;

首先不管前置++,还是后置++,a的值肯定都会+1,那现在++a前置,先++后使用,返回++之后的值,所以b也是6。

那我们来重载一下:

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

很简单,直接复用+=。

那我们再来重载后置++:

后置++呢,是先使用,后++,即返回++之前的值。那我们还是可以借助一个临时对象。
但是呢?
这里会发现有一个问题:
在这里插入图片描述
前置++和后置++没法区分啊,它们的参数和函数名是不是一样啊。

欸,那这怎么解决啊?

🆗,那当然是有办法的。
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载。C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递(它的作用就是为了构成重载),编译器自动传递。
在这里插入图片描述
所以呢,这样搞就行了。

我们来实现一下:

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

🆗,我们就搞定了。

那实现完了,大家看一下:

前置++和后置++那个效率会高一点。
是不是前置啊,因为与后置相比,前置没有拷贝啊对不对。
所以,对于内置类型来说,大家可以认为前置后置没有区别;但是对于自定义类型来说,能用前置,尽量用前置++。

我们的前置++和后置++就搞定了,那在调用的地方呢,我们正常去用就行了,编译器会自动识别匹配。
遇到前置++,它就会自动去调用前置++;遇到后置++,编译器会自动传一个整型参数,去调用后置++。

4. 日期-天数(-/-=)

上面我们通过重载+进而实现计算一个日期+一个天数之后是什么日期,那是不是还可以搞一下日期-天数,计算它的前多少天是什么日期。

那我们先来重载-=:

那思路和上面加一个天数也是类似的,我们先用日减去传过来的天数,如果减完之后<=0,说明当前日期是不合法的,那怎么办,就去加上一个月的天数,然后月–,当然要注意判断会不会减到上一年,然后还<=0的话,就继续,知道日>0循环结束。

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

我们-是复用-=的,所以这里在-=里面处理。
-=一个-100就相当于+=一个100嘛。
在这里插入图片描述

那同样对于+和+=我们也要处理一下:

在这里插入图片描述
在这里插入图片描述

那我们来处理一下:

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

这样是不是就行了,+=一个-100就相当于-=一个100嘛。
在这里插入图片描述

5. 前置- -和后置- -的重载

那有了上面的练习,再实现前置- -和后置- -不是soeasy嘛。

前置- -:

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

先- -,后使用,返回- -之后的值。
在这里插入图片描述

后置- -:

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

先使用,后- -,返回- -之前的值。
在这里插入图片描述

6. 日期-日期

上面我们搞了日期加减天数,那两个日期相减是不是也有意义啊,可以得出这两个日期之间差了多少天。

那如何计算两个日期之间相差的天数呢?

🆗,那这里给大家提供一种比较好实现的思路:
我们拿到两个日期之后,先把较小的那个日期找出来,然后用一个计数器,统计较小日期++的次数,当两个日期相等时停止,++的次数就是相差的天数。
另外如果是第一个日期大于第二个,结果应该是整数;如果第二个大于第一个,应该是负数。

我们来实现一下:

//日期-日期
int Date::operator-(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;
}

在这里插入图片描述
在这里插入图片描述

没有问题。

7. 流插入<<重载

那我们现在打印一个日期类对象的时候是不是都是去调用我们写的Print函数啊,那我们能不能想个办法打印日期类也能直接像这样打印:
在这里插入图片描述
使用我们之前学的cout+<<去打印。

大家记不记得:

我们之前文章里学习C++输入输出的时候,其实只是浅浅的提了一下如何去用,并没有对cout、<<和>>进行过多讲解。
因为以我们那时的知识储备还不能很好的理解:

在这里插入图片描述

那我们现在就可以再尝试多理解一点东西:

我们发现我们的自定义类型想用这些运算符是不是都要重载啊,除了赋值运算符以及取地址运算符,因为赋值重载和取地址重载是类的6个默认成员函数之中的,我们不写编译器会自动生成一个。但是在有些情况下根据实际情况我们还是需要自己写。
🆗,这是我们上一篇学习的知识,不用过多说明了。

然后呢我们还说C++里这样输入输出可以自动识别类型。

那为啥可以自动识别类型,其实是因为它这里对<<进行了函数重载。
在这里插入图片描述
为什么我们的内置类型可以直接用,因为库里面已经对这些类型都进行了重载,所以可以自动识别类型,是根据操作数的不同类型进行匹配。

那我们现在自定义类型想用怎么办,是不是需要自己重载啊。

那我们接下来就重载一下<<好吧:

void operator<<(ostream& out);

那想用<<是不是得使用cout<<两个操作数,一个cout,一个是我们要打印得对象,cout是啥?
🆗,上面说了它是一个ostream 类型得对象,所以我们把cout作为参数传过去。
那这里out就用来接收cout,this指针指向得就是我们要打印得类对象。

来实现一下:

void Date::operator<<(ostream& out)
{
	out << _year << _month << _day << endl;
}

这样是不是就行了啊,它的成员变量都是内置类型,我们就可以使用<<直接打印了。

我们测试一下:
在这里插入图片描述

但是我们使用的时候发现报错了。

这里操作数是不是写反了,为什么?

对于函数重载来说,两个参数的话,第一个参数是左操作数,第二个参数是右操作数。
但是对于类成员函数来说,第一个参数是不是隐藏的this指针啊,它指向我们调用该函数的对象,所以这里第一个参数是Date对象的地址。

那我们要调用的话,应该这样写:
在这里插入图片描述

但是这样写是不是好别扭啊。

怎么让它像内置类型那样使用啊。

那我们就要想办法让cout成为第一个参数,怎么做呢?
把函数重载写到类外面不就行了是吧。
没有说运算符重载不能写到类外面啊,我们写到类里面是为了啥,是不是只是为了可以访问private修饰的成员变量啊

那我们现在重载到外面:

在这里插入图片描述
那现在面临的问题是啥?
是在类外不能访问私有的成员变量,那怎么办?
可以把成员变量变成共有的public,但这样是不是就不能很好的保证封装性了;
或者可以提供Get方法,但C++一般不喜欢这样搞。
那还有什么方法呢?
🆗,就是用友元函数
那怎么做呢?
我们把函数声明再放到类里面一份,但在前面加一个friend就行了:
在这里插入图片描述

那这下我们就可以正常使用<<了:
在这里插入图片描述

但是呢:
在这里插入图片描述

这里不支持连续的流插入。为什么呢?
因为我们重载的没有返回值
在这里插入图片描述
那应该返回啥?
是不是返回cout呀,让它能够继续打印后面的。
我们看其实库里面就是这么搞的:
在这里插入图片描述

ostream& operator<<(ostream& out, Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}

在这里插入图片描述

这样就行了。

8. 流提取>>重载

那紧接着,我们再来重载一下流提取:

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

测试一下:
在这里插入图片描述
就完成了。

9. 总结

那最后呢,还要给大家说明一点:

我们在之前的类和对象第一篇其实就提到了:
在这里插入图片描述
就是类的成员函数如果直接定义在类里面,编译器是会将其当成内联函数的,不管你有没有加inline关键字。

那我们在学习内联函数的时候也说了:

一般建议将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数实现成内联函数。
所以说,类里面的成员函数如果规模较小,适合做内联函数的,直接定义在类里面。

不过我们今天写的这个日期类,里面我是所有成员函数的声明和定义都分离了,大家以后可以根据实际情况,有些成员函数直接定义在类里面。
但是我们说了内联函数不是只是对编译器的一个建议嘛,如果规模较大的函数就算我们实现成内联函数编译器也不一定按照内联函数处理。
那在类里面也是这样,就算我们把全部的类成员函数都直接定义在类里面,也不一定会全部当做内联函数处理,编译器也还是会看它具体适不适合做内联。

另外还有一点:

上一篇文章我们不是还学习了const成员函数嘛,大家还可以看一看我们日期类的这么多成员函数,哪一个在函数内部不需要改变调用它的对象,是不是把它实现成const成员函数也是比较好的。

10. 源码展示

最后,把完整的源码给大家展示一下:

  1. Date.h
#pragma once
#include <iostream>
#include <stdbool.h>
#include <assert.h>
using namespace std;

class Date
{
	//友元
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	int GetMonthDay(int year, int month);
	//构造函数
	Date(int year = 1, int month = 1, int day = 1);
	//拷贝构造函数
	//Date(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);
	void Print();
	bool operator==(const Date& d);
	Date& operator+=(int day);
	Date operator+(int day);
	//前置++
	Date& operator++();
	//后置++
	Date operator++(int);
	Date& operator-=(int day);
	Date operator-(int day);
	Date& operator--();
	Date operator--(int);
	//日期-日期
	int operator-(Date d);

	//赋值重载(对于Date类用默认生成的就行)
	//d1=d2(this就是d1,d就是d2)
	/*Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}*/
private:
	int _year;
	int _month;
	int _day;
};
//<<
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
  1. Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"

int Date::GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	int MonthArr[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;
	}
	else
	{
		return MonthArr[month];
	}
}
//构造函数
Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13 
		&& day>0 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}
}
//拷贝构造函数
//Date::Date(const Date& d)
//{
//	_year = d._year;
//	_month = d._month;
//	_day = d._day;
//}
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;
	return false;
}
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);
}
void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

//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;
//}
//
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;
//	return *this;
//}

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)
{
	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-=(int day)
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	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;
}
//日期-日期
int Date::operator-(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;
}
//<<
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
  1. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"

void Datetest1()
{
	Date d1(2023, 2, 15);
	d1.Print();
	Date d2 = d1 + (-100);
	d2.Print();
	/*Date d3 = ++d1;
	d3.Print();*/
}
void Datetest2()
{
	Date d1(2023, 2, 16);
	d1.Print();
	Date d2 = d1--;
	d1.Print();
	d2.Print();
	
}
void Datetest3()
{
	Date d1;
	cin >> d1;
	cout << d1 << endl;

}
int main()
{
	//Datetest3();
	return 0;
}

🆗,那这篇文章就到这里,欢迎大家指正!!!
下一篇文章,我们会对类和对象再做一些补充和收尾!!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript - 函数

文章目录一、箭头函数二、函数名三、理解参数3.1 箭头函数中的参数四、没有重载五、默认参数值5.1 默认参数作用域与暂时性死区六、参数扩展与收集6.1 扩展参数6.2 收集参数七、函数声明与函数表达式八、函数作为值九、函数内部9.1 arguments9.2 this9.3 caller9.4 new.target十…

关于机器人状态估计(12)-VIO/VSLAM的稀疏与稠密

VIO三相性与世界观室内ALL IN ONE 首先以此链接先对近期工作的视频做个正经的引流&#xff0c;完成得这么好的效果&#xff0c;仅仅是因为知乎限流1分钟以内的视频&#xff0c;导致整个浏览量不到300&#xff0c;让人非常不爽。 这套系统已经完成了&#xff0c;很快将正式发布…

总是跳转到国内版(cn.bing.com)?New Bing使用全攻略

你是否想要使用强大的&#xff08;被削后大嘘&#xff09;New Bing&#xff1f; 你是否已经获得了New Bing的使用资格&#xff1f; 你是否在访问www.bing.com/new时提示页面不存在&#xff1f; 你是否在访问www.bing.com时总是重定向到cn.bing.com而使用不了New Bing? New Bi…

C++——C++11第二篇

目录 可变参数模板 lambda表达式 lambda表达式语法 捕获列表说明 可变参数模板 可变参数&#xff1a;可以有0到n个参数&#xff0c;如之前学过的 Printf C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板 模板参数包 // Args是一个模板参数包&…

Python3 pip

Python3 pip pip 是 Python 包管理工具&#xff0c;该工具提供了对 Python 包的查找、下载、安装、卸载的功能。 软件包也可以在 https://pypi.org/ 中找到。 目前最新的 Python 版本已经预装了 pip。 注意&#xff1a;Python 2.7.9 或 Python 3.4 以上版本都自带 pip 工具…

IM 即时通讯实战:环信Web IM极速集成

前置技能 Node.js 环境已搭建。npm 包管理工具的基本使用。Vue2 或者 Vue3 框架基本掌握或使用。 学习目标 项目中集成 IM 即时通讯实战利用环信 IM Web SDK 快速实现在 Vue.js 中发送出一条 Hello World! 一、了解环信 IM 什么是环信 IM&#xff1f; 环信即时通讯为开发者…

深度学习神经网络基础知识(一) 模型选择、欠拟合和过拟合

专栏&#xff1a;神经网络复现目录 深度学习神经网络基础知识(一) 本文讲述神经网络基础知识&#xff0c;具体细节讲述前向传播&#xff0c;反向传播和计算图&#xff0c;同时讲解神经网络优化方法&#xff1a;权重衰减&#xff0c;Dropout等方法&#xff0c;最后进行Kaggle实…

机器学习算法原理之k近邻 / KNN

文章目录k近邻 / KNN主要思想模型要素距离度量分类决策规则kd树主要思想kd树的构建kd树的搜索总结归纳k近邻 / KNN 主要思想 假定给定一个训练数据集&#xff0c;其中实例标签已定&#xff0c;当输入新的实例时&#xff0c;可以根据其最近的 kkk 个训练实例的标签&#xff0c…

5.5 配置路由反射器

5.3.2配置路由反射器 1. 实验目的 熟悉路由反射器的应用场景掌握路由反射器的配置方法2. 实验拓扑 实验拓扑如图5-5所示: 图5-5:配置路由反射器 3. 实验步骤 (1) 配置IP地址 R1的配置 <Huawei>sy…

JVM学习笔记三:运行时数据区之程序计数器

目录 概述 字节码取指令举例 CPU时间片 经典问题 使用PC寄存器存储字节码指令地址有什么用呢&#xff1f; 为什么使用PC寄存器记录当前线程的执行地址呢&#xff1f; 概述 运行时数据区中运行速度最快的存储区域&#xff0c;并且是线程私有的&#xff0c;每一个线程都具…

在线教育有什么优势?

AI 1、便捷性&#xff1a;在线教育可以让学生在家里或者其他任何地方学习&#xff0c;不受时间和地点的限制&#xff0c;可以随时随地学习&#xff0c;极大的方便了学习者。 2、节约成本&#xff1a;在线教育可以节约学习者的时间和金钱&#xff0c;学习者可以节省出去上学的…

【MFC】数据库操作——ODBC(20)

ODBC:开放式数据库连接&#xff0c;是为解决异构数据库&#xff08;不同数据库采用的数据存储方法不同&#xff09;共享而产生的。ODBC API相对来说非常复杂&#xff0c;这里介绍MFC的ODBC类。 添加ODBC用户DSN 首先&#xff0c;在计算机中添加用户DSN&#xff1a;(WIN10下&a…

详解js在事件中,如何传递复杂数据类型(数组,对象,函数)

文章目录 前言一、何谓预编译&#xff0c;变量提升&#xff1f;二、复杂数据类型的传递 1.数组2.对象3.函数总结前言 在JavaScript这门编程语言学习中&#xff0c;如何传参&#xff0c;什么是变量提升&#xff0c;js代码预编译等等。要想成为一名优秀的js高手&#xff0c;这些内…

一个页面分成几块展示

每一项占用一个div,里面展示的是具体的图文内容,页面底部展示的是当前页码和总数,实现效果如下: 代码如下: <div class"header"></div><div class"main-content"><divclass"equipment-item"v-for"item in equipmentL…

OpenCV-PyQT项目实战(8)项目案例03:鼠标定位

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战&#xff08;3&#xff09;信号与槽机制 …

信创引领丨呼叫中心加速适配国产化

随着信创产业的高速发展&#xff0c;企业服务软件高度适配国产操作系统成为大势所趋。早在2020年&#xff0c;佳信客服平台就正式通过“华为云鲲鹏云服务”平台系统的兼容性测试与认证&#xff0c;成为鲲鹏凌云伙伴&#xff0c;也意味着佳信客服平台正式与国产硬件平台及操作系…

【Kubernetes 企业项目实战】07、最新一代微服务网格 Istio 入门到企业实战(下)

目录 一、istio 核心资源解读 1.1 Gateway 总结&#xff1a; 1.2 VirtualService 1.2.1 VirtualService 主要由以下部分组成 1.2.2 virtualservice 配置路由规则 1.2.3 路由规则优先级 1.2.4 多路由规则解读 1.3 DestinationRule 二、istio 核心功能演示 2.1 断路器…

【离散数学】1. 数理逻辑

1.数理逻辑 2. 集合论 3. 代数系统 4. 图论 离散数学&#xff1a;研究离散量结构及相互关系的学科 数理逻辑集合论代数系统图论 逻辑&#xff1a;研究推理的科学 数学方法&#xff1a;引进一套符号系统的方法 数理逻辑是用数学方法研究形式逻辑的科学&#xff0c;即使用符号化…

vue环境总结

因将node.js升级后&#xff0c;打包运行出错了。后来加班重新改好。 一、用nvm管理可以用命令将node和npm对应安装 1.首先在控制台输入where node查看之前本地安装的node的路径 2.将node目录删除或者卸载 二、1.安装nvm 从官网下载安装包 https://github.com/coreybutler/nvm…

JDK定时器Timer原理

前言 前些时间想到利用redis实现延时队列&#xff0c;但是底层的定时器不止如何实现好些&#xff0c;故此研究了一下jdk的Timer。 Timer是一个用于执行定时任务的类&#xff0c;可以单次执行或按指定时间间隔循环执行&#xff08;直到主动cancel或线程被杀掉&#xff09;。Ti…