日期类的实现(C++)

 个人主页:Jason_from_China-CSDN博客

所属栏目:C++系统性学习_Jason_from_China的博客-CSDN博客

所属栏目:C++知识点的补充_Jason_from_China的博客-CSDN博客

前言

日期类是六个成员函数学习的总结和拓展,是实践的体现

创建文件

构造函数

//.h文件
#pragma once
#include<iostream>//流文件
#include<assert.h>//断言头文件
using namespace std;//域展开

class Date
{

public:
	//构造函数
	Date(int year = 1901, int month = 1, int day = 1);
    //打印函数
	void print();
private:
	int _year;
	int _month;
	int _day;
};


//.cpp文件
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
	:_year(year)
	, _month(month)
	, _day(day)
{}
//打印函数
void Date::print()
{
	cout << _year << "/" << _month << "/" << _day << endl << endl;
}


//测试文件
#include"Date.h"
int main()
{
	//日期类构造函数的测试
	Date d1(111,1,1);
	d1.print();

	return 0;
} 

这里顺手实现一个打印函数, 方便调试,测试一下没有问题

析构函数

日期类是不需要手动实现析构函数的,因为这里是没有开辟的动态空间,默认生成的析构函数已经完全够用

拷贝构造

//拷贝构造函数
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
  • 实现逻辑:
    • _year = d._year;:将传入对象d的年份赋值给新创建对象的年份成员变量。
    • _month = d._month;:将传入对象d的月份赋值给新创建对象的月份成员变量。
    • _day = d._day;:将传入对象d的日期赋值给新创建对象的日期成员变量。
 

通过这三个赋值操作,新创建的Date对象的成员变量被初始化为与传入对象完全相同的值,从而实现了对象的拷贝

运算符重载比较日期的综合实现

等于的实现(==)

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

实现逻辑

 
  1. 首先比较两个对象的年份_year是否相等,如果不相等则直接返回false,表示两个日期不同。
  2. 如果年份相等,接着比较月份_month是否相等。如果不相等,同样返回false
  3. 如果年份和月份都相等,最后比较日期_day是否相等。如果不相等,返回false
  4. 如果年份、月份和日期都相等,那么返回true,表示两个日期对象相等。

小于的实现(<)

bool Date::operator<(const Date& d)const
{
	if (this->_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;
}

实现逻辑

  1. 首先比较两个日期对象的年份。如果当前对象的年份_year小于传入对象的年份d._year,说明当前对象的日期更早,直接返回true
  2. 如果年份相等,接着比较月份。如果当前对象的月份_month小于传入对象的月份d._month,说明当前对象的日期仍然更早,返回true
  3. 如果年份和月份都相等,最后比较日期。如果当前对象的日期_day小于传入对象的日期d._day,说明当前对象的日期更早,返回true
  4. 如果以上三个条件都不满足,即年份、月份和日期都不小于传入对象相应的值,说明当前对象的日期不早于传入对象的日期,返回false

小于等于实现(<=)

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

实现逻辑

  1. return *this < d || *this == d;这行代码使用了逻辑或(||)运算符来判断当前对象是否小于等于传入的对象。
    • *this < d表示调用已经重载的小于运算符<,判断当前对象是否小于传入的对象。如果这个条件为真,那么当前对象肯定小于等于传入的对象,直接返回true
    • *this == d表示调用已经重载的等于运算符==,判断当前对象是否等于传入的对象。如果当前对象不小于传入的对象(即*this < d为假),但两个对象相等,那么当前对象也小于等于传入的对象,同样返回true
    • 如果以上两个条件都不满足,即当前对象既不小于传入的对象也不等于传入的对象,那么返回false,表示当前对象大于传入的对象,不满足小于等于的条件。

大于的实现(>) 

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

实现逻辑

  1. return!(*this <= d);这行代码利用了逻辑非(!)运算符和已经实现的小于等于运算符<=来判断当前对象是否大于传入的对象。
    • *this <= d会调用已经重载的小于等于运算符,判断当前对象是否小于等于传入的对象。如果这个条件为真,说明当前对象不大于传入的对象。
    • *this <= d取逻辑非,即!(*this <= d)。如果*this <= d为假,那么逻辑非的结果为真,这就表示当前对象大于传入的对象,直接返回true;如果*this <= d为真,那么逻辑非的结果为假,这就表示当前对象不大于传入的对象,返回false

 大于等于的实现(>=)

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

实现逻辑

  1. return!(*this < d);这行代码利用了逻辑非(!)运算符和已经实现的小于运算符<来判断当前对象是否大于等于传入的对象。
    • *this < d会调用已经重载的小于运算符,判断当前对象是否小于传入的对象。如果这个条件为真,说明当前对象小于传入的对象,那么当前对象就不大于等于传入的对象。
    • *this < d取逻辑非,即!(*this < d)。如果*this < d为假,那么逻辑非的结果为真,这就表示当前对象不小于传入的对象,也就是大于等于传入的对象,直接返回true;如果*this < d为真,那么逻辑非的结果为假,这就表示当前对象小于传入的对象,不满足大于等于的条件,返回false

代码实现

//.h文件
#pragma once
#include<iostream>//流文件
#include<assert.h>//断言头文件
using namespace std;//域展开

class Date
{

public:
	//构造函数
	Date(int year = 1901, int month = 1, int day = 1);
    //打印函数
	void print();

    //运算符重载
	bool operator==(const Date& d)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;

private:
	int _year;
	int _month;
	int _day;
};


//.cpp实现文件
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
	:_year(year)
	, _month(month)
	, _day(day)
{}
//打印函数
void Date::print()
{
	cout << _year << "/" << _month << "/" << _day << endl << endl;
}

//运算符重载
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 (this->_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)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);
}



//测试文件
#include"Date.h"
int main()
{
	//日期类构造函数的测试
	Date d1(111,1,1);
	d1.print();

	//日期类拷贝构造函数的测试
	Date d2(d1);
	d2.print();

	//日期类获取每个月天数的测试
	int ret1 = d2.GetmonthDay(1999, 2);
	cout << ret1 << endl << endl;

	//比较测试(没有问题)
	Date d3(1, 1, 1);
	Date d4(1, 1, 1);
	Date d5(2, 2, 2);
	bool ret = d3.operator==(d4);
	bool ret2 = d3.operator<(d5);
	bool ret3 = d3.operator<=(d4);
	bool ret4 = d5.operator<=(d3);
	bool ret5 = d5.operator<(d3);
	cout << ret << endl << endl;
	cout << ret2 << endl << endl;
	cout << ret3 << endl << endl;
	cout << ret4 << endl << endl;
	cout << ret5 << endl << endl;
	return 0;
} 

测试:没有问题

获取某一年的某一月份的天数(为日期的加减做下铺垫)

我们进行计算的时候是需要找到某一年的某一月份的天数,然后才能进行加减

这里我们直接写成内联函数,在类里面实现,因为每次计算都需要进行计算时间,我们直接重载为成员函数,这样空间就是开辟好的,节约时间,这也是C++比其他语言迅速的原因,不仅仅是实现上面,还有平时书写习惯上面

  • 这里一个关键点就是getmonthday没有做定义和声明的分离,因为这里超频繁的调用,所以我们定义为内联,频繁的调用定义为内联提高效率
  • 这里返回的是月份的天数
  • 这里判断的时候,有一个小细节,就是先判断月份,因为先判断月份比较简单,后判断年份,这样会稍微提高速度
//这里是放到类里面的	
//获取一个日期(内联函数)
	int GetmonthDay(int year, int month)const
	{
		assert(month < 13 && month>0);
		static int monthDayArray[13] = { -1,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 monthDayArray[month];
		}
	}

如果是闰年就单独判断一下,四年一润,百年不润,四百年润一次,并且同时月份是二月(每四年会多一天,365天多五小时+),对逻辑进行修改(因为是不是二月比计算更容易判断)

运算符重载+=,+和-=,-的实现

+=的实现

//计算加法
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= day;
	}
	_day += day;
	while (_day > GetmonthDay(_year, _month))
	{
		_day -= GetmonthDay(_year, _month);
		_month += 1;
		if (_month > 12)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}

实现逻辑

  1. 首先判断传入的天数day是否小于 0,如果是,则调用减法赋值运算符(-=)来处理负数的情况,实现了一种统一的处理方式,避免了重复编写类似的代码。
  2. 如果day大于等于 0,则将其加到当前日期对象的天数_day上。
  3. 然后进入一个循环,检查当前日期的天数_day是否大于当前年份和月份对应的天数(通过调用GetmonthDay(_year, _month)获取)。
    • 如果是,说明当前日期已经超出了当月的天数范围。此时,将当前日期的天数减去当月的天数,即_day -= GetmonthDay(_year, _month),然后月份_month加 1。
    • 如果月份_month超过了 12,说明已经到了下一年,此时将年份_year加 1,并将月份重置为 1。
  4. 循环继续检查新的日期是否仍然超出当月的天数范围,直到日期合法为止。
  5. 最后,返回当前日期对象的引用,以便支持连续的加法赋值操作。

+的实现(复用+=)

Date Date::operator+(int day)const
{
	Date tmp = *this;
	tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化
	return tmp;
}

实现逻辑

  1. Date tmp = *this;:创建一个临时的Date对象tmp,并使用当前对象(*this)进行初始化。这样就复制了当前对象的状态到tmp中。
  2. tmp += day;:调用已经重载的加法赋值运算符+=,在临时对象tmp上加上指定的天数day。这个操作会修改tmp的日期,但不会影响原来的对象(因为tmp是一个对象)。
  3. return tmp;:返回修改后的临时对象tmp,作为加上指定天数后的新日期对象。

-=的实现

//日期类的实现-=
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += day;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month -= 1;
		if (_month == 0)
		{
			_year -= 1;
			_month = 12;
		}
		_day += GetmonthDay(_year, _month);
	}
	return *this;
}

实现逻辑

  1. 首先判断传入的天数day是否小于 0,如果是,则调用加法赋值运算符(+=)来处理负数的情况,实现了一种统一的处理方式,避免了重复编写类似的代码。
  2. 如果day大于等于 0,则将其从当前日期对象的天数_day中减去。
  3. 然后进入一个循环,检查当前日期的天数_day是否小于等于 0。
    • 如果是,说明当前日期已经超出了合法范围,需要进行调整。首先将月份_month减 1。
    • 如果月份_month减到 0,说明已经到了上一年,此时将年份_year减 1,并将月份重置为 12。
    • 接着,将减去的天数加上当前调整后的年份和月份对应的天数(通过调用GetmonthDay(_year, _month)获取),以确保日期的合法性。
  4. 循环继续检查新的日期是否仍然小于等于 0,直到日期合法为止。
  5. 最后,返回当前日期对象的引用,以便支持连续的减法赋值操作。

 -的实现(复用-=)

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

实现逻辑

  1. Date tmp = *this;:创建一个临时的Date对象tmp,并使用当前对象(*this)进行初始化。这样就复制了当前对象的状态到tmp中。
  2. tmp -= day;:调用已经重载的减法赋值运算符-=,在临时对象tmp上减去指定的天数day。这个操作会修改tmp的日期,但不会影响原来的对象(因为tmp是一个副本)。
  3. return tmp;:返回修改后的临时对象tmp,作为减去指定天数后的新日期对象。

代码实现

//.h文件
#pragma once
#include<iostream>//流文件
#include<assert.h>//断言头文件
using namespace std;//域展开

class Date
{

public:
	//构造函数
	Date(int year = 1901, int month = 1, int day = 1);
    //打印函数
	void print();

    //运算符重载
	bool operator==(const Date& d)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);
	Date operator+(int day)const;


	//计算减法
	Date& operator-=(int day);
	Date operator-(int day)const;
private:
	int _year;
	int _month;
	int _day;
};







//.cpp实现文件
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
	:_year(year)
	, _month(month)
	, _day(day)
{}
//打印函数
void Date::print()
{
	cout << _year << "/" << _month << "/" << _day << endl << endl;
}

//运算符重载
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 (this->_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)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);
}

//计算加法
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= day;
	}
	_day += day;
	while (_day > GetmonthDay(_year, _month))
	{
		_day -= GetmonthDay(_year, _month);
		_month += 1;
		if (_month > 12)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)const
{
	Date tmp = *this;
	tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化
	return tmp;
}
//日期类的实现-=
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += day;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month -= 1;
		if (_month == 0)
		{
			_year -= 1;
			_month = 12;
		}
		_day += GetmonthDay(_year, _month);
	}
	return *this;
}
Date Date::operator-(int day)const
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}





//测试文件
#include"Date.h"
int main()
{
	//日期类构造函数的测试
	Date d1(111,1,1);
	d1.print();

	//日期类拷贝构造函数的测试
	Date d2(d1);
	d2.print();

	//日期类获取每个月天数的测试
	int ret1 = d2.GetmonthDay(1999, 2);
	cout << ret1 << endl << endl;

	//比较测试(没有问题)
	Date d3(1, 1, 1);
	Date d4(1, 1, 1);
	Date d5(2, 2, 2);
	bool ret = d3.operator==(d4);
	bool ret2 = d3.operator<(d5);
	bool ret3 = d3.operator<=(d4);
	bool ret4 = d5.operator<=(d3);
	bool ret5 = d5.operator<(d3);
	cout << ret << endl << endl;
	cout << ret2 << endl << endl;
	cout << ret3 << endl << endl;
	cout << ret4 << endl << endl;
	cout << ret5 << endl << endl;

	//测试日期类的加等和加
	Date d6;
	d6 += 1000;
	d6.print();
	Date ret6 = d6 + 10000;
	ret6.print();
	
	//测试日期类的减等和减
	Date d7;
	d7 -= 10000;
	d7.print();
	Date d8;
	Date ret8 = d8 - 1000;
	ret8.print();
	return 0;
} 

测试:没有问题

 

为什么有一个判断是否是负值的判断:

这里提醒一下,规范(+,+=,-,-=)的格式,防止因为传参传递的是负数从而导致计算出现误差,简单的说当是+=的时候传递的数值是负值,我们直接去调用-=

运算符重载++和--的实现

前置和后置的区别

我们以++来说明 

一般我们会给后置类型加上一个参数

也就是:

当然这里你也可以传递0或者1

前置++

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

实现逻辑

 
  1. return *this += 1;这行代码通过调用已经重载的加法赋值运算符+=来实现日期的自增。这里将整数 1 作为参数传递给+=运算符,表示将日期增加一天。
  2. *this表示当前的日期对象,通过调用+=运算符,在当前日期对象上加上一天。这个操作会修改当前对象的日期状态。
  3. 最后,返回当前日期对象的引用,以便支持连续的自增操作。

后置++

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

实现逻辑

 
  1. Date tmp = *this;:创建一个临时的Date对象tmp,并使用当前对象(*this)进行初始化。这样就复制了当前对象的状态到tmp中,保存了自增前的日期对象。
  2. *this += 1;:调用已经重载的加法赋值运算符+=,在当前对象上加上一天,实现日期的自增操作。
  3. return tmp;:返回临时对象tmp,即自增前的日期对象副本。这里的关键在于对象本身进行+,但是返回的是创建的临时对象

前置--

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

实现逻辑

 
  1. return *this -= 1;这行代码通过调用已经重载的减法赋值运算符-=来实现日期的自减。这里将整数 1 作为参数传递给-=运算符,表示将日期减少一天。
  2. *this表示当前的日期对象,通过调用-=运算符,在当前日期对象上减去一天。这个操作会修改当前对象的日期状态。
  3. 最后,返回当前日期对象的引用,以便支持连续的自减操作。

后置--

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

实现逻辑

 
  1. Date tmp = *this;:创建一个临时的Date对象tmp,并使用当前对象(*this)进行初始化。这样就复制了当前对象的状态到tmp中,保存了自减前的日期对象。
  2. *this -= 1;:调用已经重载的减法赋值运算符-=,在当前对象上减去一天,实现日期的自减操作。
  3. return tmp;:返回临时对象tmp,即自减前的日期对象副本。

 计算日期之间相差多少天

这里有坑,这里有坑,这里有坑

首选我们直接上代码,因为代码是很简单的,但是有一些语法结构是有坑的

//日期之间相差多少天
int Date::operator-(const Date& d)const
{
	//这里采取假设法,假设this是大的,如果不是,则进行转换
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int cout = 0;
	while (max != min)
	{
		++min;
		++cout;
	}
	return cout* flag;
}

//测试
int main()
{
    Date ret6 = d6 + 10000;
    ret6.print();
    //首先,编译器尝试寻找一个可以处理d6 + 10000的操作符重载函数,比如operator + (int),
    //如果没有找到这样的函数。
    //然后,编译器可能会尝试将d6 + 10000解释为d6.operator+(Date(10000)),
    //也就是先通过单参数构造函数将整数10000转换为一个临时的Date对象,
    //然后再尝试调用可能存在的operator + (const Date&)成员函数来执行加法操作。
    //
    // 两个解决办法
    // 1, explicit Date(int value);
    //使用explicit关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免了意    外的行为。
    // 2,加上const修饰
 

	//测试日期类的减等和减
	Date d7;
	d7 -= 10000;
	d7.print();
	Date d8(2000,1,1);
	Date ret8 = d8 - 1000; //原因是:1000但参构造成为了Date对象,导致调用不明确 禁止掉可以解决折扣的问题
	ret8.print(); //如果你给那个函数加上了const就只有const对象可以调用  不会出现调用不明确了

    RETURN 0;
}

逻辑注意:

  1. 首先我们实现逻辑的时候,不能真的这个用日期和日期直接相互减减,因为那样会很麻烦
  2. 所以我们可以直接利用追击问题,当数值一样的时候,我们就可以跳出循环

实现逻辑

  1. 首先进行大小判断和初始化:
    • Date max = *this;Date min = d;以及int flag = 1;这几行代码假设当前对象(*this)是较大的日期,将其赋值给max,传入的参数对象d赋值给min,并设置标志变量flag为 1。
    • 如果当前对象实际上小于传入的对象,即*this < d,则进行交换:max = d;min = *this;,同时将标志变量flag设为 -1。这样确保max始终是较大的日期,min始终是较小的日期。
  2. 然后计算日期差:
    • 使用一个循环while (max!= min),只要两个日期不相等就继续循环。
    • 在循环中,每次将较小的日期min递增一天(这里假设Date类实现了自增运算符++,使其能够正确地增加一天,同时处理月份和年份的变化),并将天数计数器cout加一。
    • 当两个日期相等时,循环结束。
  3. 最后返回结果:
    • 将天数计数器cout乘以标志变量flag,得到最终的日期差。如果一开始假设正确,即当前对象较大,那么返回正数的日期差;如果当前对象较小,返回负数的日期差。

这里有坑的点:

所以还是回到那一句话,const应加尽加

解决办法一:使用 explicit 关键字

 

在单参数构造函数上面加上 explicit 关键字,例如 explicit Date(int value)。使用 explicit 关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免可能出现的调用歧义。

 

解决办法二:对成员函数进行 const 修饰

 

对于 int operator-(const Date& d) const;这个成员函数加上 const 修饰。这里加上 const 修饰可以解决调用歧义的原因是:这个函数的返回值是 int 类型,也就是常量。常量具有不可修改的特性,所以就不会导致调用歧义。

 C++流插入(输出到屏幕上面)和流提取(输入)

方式1:重载为成员函数

这里解释一下,重载为成员函数是可以的,但是是不符合我们的使用习惯,我这里用图解解释一下,了解就可以,逻辑思路都一样

 方式2(使用):友元函数

这里是比较多的使用方式,友元函数也是比较简单的,只需要再函数的公有区域加上

friend+函数全称;

流插入(输出)(重载为全局函数):

//流插入(输出到屏幕)//给返回值,支持连续赋值,不然只能一次打印一个
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl;
	return out;
}

注意

  1. 这里的out是cout的简称
  2. 这里我们需要有返回值的目的是,支持链式操作,也就是输入可以连续输入,输出可以连续输出,没有返回值只能实现单次
  3. 有返回值能实现链式操作,因为对于赋值运算而言赋值是从右往左实现的,但是对于插入流而言是从左往右实现的 ,每次都会返回cout从而进行下一次的操作

实现逻辑

 
  1. out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl;这行代码将日期对象d的年份、月份和日期按照特定格式输出到输出流out中。
    • 首先输出年份d._year,并紧跟一个 “年” 字。
    • 接着输出月份d._month,并紧跟一个 “月” 字。
    • 然后输出日期d._day,并紧跟一个 “日” 字。
    • 最后输出两个换行符endl << endl,用于在输出日期后进行换行,使输出更加清晰。
  2. return out;返回输出流对象的引用,以便支持连续输出操作。

流提取(输入)(重载为全局函数):

代码实现:

//流提取(提取输入到类里面,所以类不能加const)
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

注意:

  1. 这里的传递过来的对象不能加上const,因为输入本身就是需要改变对象的,所以是不能加上const的

实现逻辑

 
  1. in >> d._year >> d._month >> d._day;这行代码从输入流in中依次读取数据,并分别赋值给Date对象d的年份、月份和日期成员变量。
    • 首先读取一个整数作为年份,存储到d._year中。
    • 接着读取一个整数作为月份,存储到d._month中。
    • 最后读取一个整数作为日期,存储到d._day中。
  2. return in;返回输入流对象的引用,以便支持连续输入操作。

日期类的检查

//这里我们直接重载为内联函数
//检查日期是否合法
bool CheckDate()
{
	//检查月份,天数
	if (_year < 0 || _month < 1 || _month>12
		|| _day < 1 || _day>this->GetmonthDay(_year, _month))
	{
		return false; 
	}
	else
	{
		return true;
	}
}

所以我们可以在哪检查,可以在输入的时候和构造的时候检查,这里我只是在提取流进行检查

//流提取(提取输入到类里面,所以类不能加const)
istream& operator>>(istream& in, Date& d)
{
	while (1)
	{
		cout << "请依次输入年月日(只支持公元前的写法):";
		in >> d._year >> d._month >> d._day;
		if (!d.CheckDate())
		{
			cout << "输入的日期类不合法,请重新输入:";
			d.print();
		}
		else
		{
			break;
		}
	}
	return in;
}

实现逻辑

 
  1. while (1):这是一个无限循环,目的是不断要求用户输入合法的日期,直到输入正确为止。
  2. cout << "请依次输入年月日(只支持公元前的写法):";:提示用户输入日期信息,并明确只支持公元前的写法。
  3. in >> d._year >> d._month >> d._day;:从输入流中读取三个整数,分别存储到Date对象d的成员变量_year(年份)、_month(月份)和_day(日期)中。
  4. if (!d.CheckDate()):调用Date类的成员函数CheckDate()来检查输入的日期是否合法。如果这个函数返回false,说明输入的日期不合法。
    • cout << "输入的日期类不合法,请重新输入:";:如果日期不合法,输出提示信息,要求用户重新输入。
    • d.print();:可能是输出当前输入的不合法日期,以便用户查看并纠正错误。
  5. else:如果日期合法。
    • break;:跳出无限循环。

 代码总结

//Date.h头文件

#pragma once
#include<iostream>//流文件
#include<assert.h>//断言头文件
using namespace std;//域展开

class Date
{
	//友元函数(输入输出流)
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	//构造函数
	Date(int year = 1901, int month = 1, int day = 1);

	//拷贝构造函数
	Date(const Date& d);

	//获取一个日期(内联函数)
	int GetmonthDay(int year, int month)const
	{
		assert(month < 13 && month>0);
		static int monthDayArray[13] = { -1,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 monthDayArray[month];
		}
	}

	//检查日期是否合法
	bool CheckDate()
	{
		//检查月份,天数
		if (_year < 0 || _month < 1 || _month>12
			|| _day < 1 || _day>this->GetmonthDay(_year, _month))
		{
			return false; 
		}
		else
		{
			return true;
		}
	}

	//运算符重载
	bool operator==(const Date& d)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;
	bool operator!=(const Date& d)const;

	//计算加法
	Date& operator+=(int day);
	Date operator+(int day)const;


	//计算减法
	Date& operator-=(int day);
	Date operator-(int day)const;

	//++ --
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
	//打印函数
	void print();
	
	//日期之间相差多少天
	int operator-(const Date& d)const;//这里加上const,就不会因为单参数构造产生调用歧义
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)
{}

//拷贝构造函数
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

//打印函数
void Date::print()
{
	cout << _year << "/" << _month << "/" << _day << endl << endl;
}

//运算符重载
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 (this->_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)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 !(*this == d);
}

//计算加法
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= day;
	}
	_day += day;
	while (_day > GetmonthDay(_year, _month))
	{
		_day -= GetmonthDay(_year, _month);
		_month += 1;
		if (_month > 12)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)const
{
	Date tmp = *this;
	tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化
	return tmp;
}
//日期类的实现-=
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += day;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month -= 1;
		if (_month == 0)
		{
			_year -= 1;
			_month = 12;
		}
		_day += GetmonthDay(_year, _month);
	}
	return *this;
}
Date Date::operator-(int day)const
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}
//前置++和后置++
Date& Date::operator++()
{
	return *this += 1;
}
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
//前置--和后置--
Date& Date::operator--()
{
	return *this -= 1;
}
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

//日期之间相差多少天
int Date::operator-(const Date& d)const
{
	//这里采取假设法,假设this是大的,如果不是,则进行转换
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int cout = 0;
	while (max != min)
	{
		++min;
		++cout;
	}
	return cout* flag;
}

//流插入(输出到屏幕)//给返回值,支持连续赋值,不然只能一次打印一个
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl;
	return out;
}
//流提取(提取输入到类里面,所以类不能加const)
istream& operator>>(istream& in, Date& d)
{
	while (1)
	{
		cout << "请依次输入年月日(只支持公元前的写法):";
		in >> d._year >> d._month >> d._day;
		if (!d.CheckDate())
		{
			cout << "输入的日期类不合法,请重新输入:";
			d.print();
		}
		else
		{
			break;
		}
	}
	return in;
}

//test.cpp测试文件

#include"Date.h"
int main()
{
	//日期类构造函数的测试
	Date d1(111,1,1);
	d1.print();

	//日期类拷贝构造函数的测试
	Date d2(d1);
	d2.print();

	//日期类获取每个月天数的测试
	int ret1 = d2.GetmonthDay(1999, 2);
	cout << ret1 << endl << endl;

	//比较测试(没有问题)
	Date d3(1, 1, 1);
	Date d4(1, 1, 1);
	Date d5(2, 2, 2);
	bool ret = d3.operator==(d4);
	bool ret2 = d3.operator<(d5);
	bool ret3 = d3.operator<=(d4);
	bool ret4 = d5.operator<=(d3);
	bool ret5 = d5.operator<(d3);
	cout << ret << endl << endl;
	cout << ret2 << endl << endl;
	cout << ret3 << endl << endl;
	cout << ret4 << endl << endl;
	cout << ret5 << endl << endl;

	//测试日期类的加等和加
	Date d6;
	d6 += 1000;
	d6.print();
	Date ret6 = d6 + 10000;
	ret6.print();
	//首先,编译器尝试寻找一个可以处理d6 + 10000的操作符重载函数,比如operator + (int),
	//如果没有找到这样的函数。
	//然后,编译器可能会尝试将d6 + 10000解释为d6.operator+(Date(10000)),
	//也就是先通过单参数构造函数将整数10000转换为一个临时的Date对象,
	//然后再尝试调用可能存在的operator + (const Date&)成员函数来执行加法操作。
	//
	// 两个解决办法
	// 1, explicit Date(int value);
	//使用explicit关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免了意外的行为。
	// 2,加上const修饰
	//

	//测试日期类的减等和减
	Date d7;
	d7 -= 10000;
	d7.print();
	Date d8(2000,1,1);
	Date ret8 = d8 - 1000; //原因是:1000但参构造成为了Date对象,导致调用不明确 禁止掉可以解决折扣的问题
	ret8.print(); //如果你给那个函数加上了const就只有const对象可以调用  不会出现调用不明确了

	//测试日期类的++和--
	Date d9;
	Date ret9 = d9--;
	ret9.print();
	++d9;
	++d9;
	d9.print();

	//流插入测试
	cout << d9;
	//流提取测试
	Date d10;
	cin >> d10;
	cout << d10;

	//测试日期之间相差多少天
	Date d11(1901, 1, 1);
	Date d12(2000, 3, 1);
	int ret10 = d11 - d12;
	cout << ret10 << endl;
	return 0;
} 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[C#]使用onnxruntime部署yolov11-onnx实例分割模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 在C#中使用ONNX Runtime部署YOLOv11-ONNX实例分割模型&#xff0c;涉及到模型的加载、数据预处理、模型推理和后处理几个关键步骤。 首先&#xff0c;需要确保已经安装了ONNX Runtime的NuGe…

whisper 实现语音识别 ASR - python 实现

语音识别&#xff08;Speech Recognition&#xff09;&#xff0c;同时称为自动语音识别&#xff08;英语&#xff1a;Automatic Speech Recognition, ASR&#xff09;&#xff0c;将语音音频转换为文字的技术。 whisper是一个通用的语音识别模型&#xff0c;由OpenAI公司开发。…

【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart

1. 后端传参重命名&#xff08;后端参数映射&#xff09; 某些特殊情况下&#xff0c;前端传递的参数 key 和我们后端接收的 key 可以不一致&#xff0c;比如前端传了一个 time 给后端&#xff0c;而后端是使用 createtime 字段来接收的&#xff0c;这样就会出现参数接收不到的…

【新人系列】Python 入门(一):介绍及环境搭建

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12801353.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Python 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…

Python数据分析-远程办公与心理健康分析

一、研究背景 随着信息技术的飞速发展和全球化的推进&#xff0c;远程工作&#xff08;Remote Work&#xff09;成为越来越多企业和员工的选择。尤其是在2020年新冠疫情&#xff08;COVID-19&#xff09;爆发后&#xff0c;全球范围内的封锁措施使得远程工作模式迅速普及。根据…

【AIGC】ChatGPT提示词Prompt高效编写模式:结构化Prompt、提示词生成器与单样本/少样本提示

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;结构化Prompt (Structured Prompt)组成元素应用实例优势结论 &#x1f4af;提示词生成器 (Prompt Creator)如何工作应用实例优势结论 &#x1f4af;单样本/少样本提示 (O…

(贪心) 反悔贪心之反悔堆

文章目录 ⭐例题&#x1f6a9;题意与思路 ⭐返回贪心&#x1f6a9;原理&#xff08;反悔池&#xff09;&#x1f6a9;落实到题&#x1f6a9;AC code ⭐练习题⭐END&#x1f31f;交流方式 ⭐例题 经典例题&#xff1a; 871. 最低加油次数 &#x1f6a9;题意与思路 题意&#xf…

Microsoft 更新 Copilot AI,未來將能使用語音並看到你瀏覽的網頁

不過受到 Recall 事件的影響&#xff0c;更新的推出將更緩慢謹慎。 Microsoft 也同步對其網頁版及行動版的 Copilot AI 進行大改版。這主要是為網頁版換上了一個較為簡單乾淨的介面&#xff0c;並增加了一些新的功能&#xff0c;像是 Copilot Voice 能讓你與 AI 助手進行對話式…

IDEA:增加类注释模板和方法注释模板

文章目录 概要配置类注释模板配置方法模版 概要 配置类注释和方法注释 配置类注释模板 点击setting->Editor->File and Code Templates&#xff0c;然后找到Class&#xff0c;如下图&#xff1a; 注意勾掉Reformat according to style&#xff0c;否则会格式化。 注…

51单片机的水位检测系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块水位传感器继电器LED、按键和蜂鸣器等模块构成。适用于水位监测、水位控制、水位检测相似项目。 可实现功能: 1、LCD1602实时显示水位高度 2、水位传感器采集水位高度 3、按键可设置水位的下限 4、按键可手动加…

指针(7)

目录 1. sizeof和strlen的对⽐ 1.1 sizeof 1.2 strlen sizeof 和 strlen 总结&#xff1a; 2. 数组和指针 2.1 ⼀维数组 2.2 字符数组 1. sizeof和strlen的对⽐ 1.1 sizeof 计算的是使⽤类型创建的变量所占内存空间的⼤⼩。sizeof不在乎你里面放的什么。sizieof是操作符…

设计模式~~~

简单工厂模式(静态工厂模式) 工厂方法模式 抽象工厂角色 具体工厂角色

王者农药更新版

GPIO简介 STM32开发板有5组GPIO引脚&#xff0c;分别是GPIOA,GPIOB,GPIOC,GPIOD,GPIOE&#xff0c;每组GPIO有16个引脚。 每个引脚都有4个位来配置其端口&#xff0c;可以配置出不同的输入\输出模式。 1、普通推挽输出&#xff08;GPIO_Mode_Out_PP&#xff09;: 使用场合&…

在不支持WSL2的Windows环境下安装Redis并添加环境变量的方法

如果系统版本支持 WSL 2 可跳过本教程。使用官网提供的教程即可 官网教程 查看是否支持 WSL 2 如果不支持或者觉得麻烦可以按照下面的方式安装 下载 点击打开下载地址 下载 zip 文件即可 安装 将下载的 zip 文件解压到自己想要解压的地方即可。&#xff08;注意&#x…

sqli-labs less-17密码重置报错注入

密码重置报错植入 来到首页面我们看到页面提示【password reset】&#xff0c;说明这是更改密码的注入&#xff0c;也就是说我们知道一个账户名&#xff0c;修改他的密码&#xff0c;所以我们可以在passwd处进行注入。 闭合方式 添加单引号 有报错 可以知道闭合方式为单引号…

Leetcode—76. 最小覆盖子串【困难】

2024每日刷题&#xff08;167&#xff09; Leetcode—76. 最小覆盖子串 C实现代码 class Solution { public:string minWindow(string s, string t) {int bestL -1;int l 0, r 0;vector<int> cnt(128);for(const char c: t) {cnt[c];}int require t.length();int m…

OJ在线评测系统 微服务 用分布式消息队列 RabbitMQ 解耦判题服务和题目服务 手搓交换机和队列 实现项目异步化

消息队列解耦 项目异步化 分布式消息队列 分布式消息队列是一种用于异步通信的系统&#xff0c;它允许不同的应用程序或服务之间传递消息。消息队列的核心理念是将消息存储在一个队列中&#xff0c;发送方可以将消息发送到队列&#xff0c;而接收方则可以在适当的时候从队列中…

安卓如何实现双击触摸唤醒点亮屏幕功能-Android framework实战开发

背景 经常有学员朋友在群里问到一个目前市场上常见的功能&#xff1a; 手机待机时候双击屏幕可以唤醒点亮手机屏幕功能 如何实现这个功能&#xff0c;经常有同学在群里求助&#xff0c;今天就刚好来讨论一下这个待机时候双击触摸唤醒点亮屏幕的功能的实现方案。 功能核心方案设…

【微服务】服务注册与发现 - Eureka(day3)

CAP理论 P是分区容错性。简单来说&#xff0c;分区容错性表示分布式服务中一个节点挂掉了&#xff0c;并不影响其他节点对外提供服务。也就是一台服务器出错了&#xff0c;仍然可以对外进行响应&#xff0c;不会因为某一台服务器出错而导致所有的请求都无法响应。综上所述&…

dwceqos网络驱动性能优化

文章介绍 本文会分享一些在QNX系统下对io-pkt-v6-hc驱动模块cpu loading过高问题优化的经验&#xff0c;以及一些调优debug的方法。这些优化措施实施之后可以降低io-pkt-v6-hc在高负载的情况下的cpu loading。本文的调优是基于synopsys公司的dwceqos模块&#xff0c;理论上方法…