日期类的实现(C++)

news2024/11/24 12:49:45

 个人主页: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;
} 

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

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

相关文章

HCIP--以太网交换安全(二)

端口安全 一、端口安全概述 1.1、端口安全概述&#xff1a;端口安全是一种网络设备防护措施&#xff0c;通过将接口学习的MAC地址设为安全地址防止非法用户通信。 1.2、端口安全原理&#xff1a; 类型 定义 特点 安全动态MAC地址 使能端口而未是能Stichy MAC功能是转换的…

在VMware WorkStation上安装飞牛OS(NAS系统)

对于NAS系统&#xff0c;小白相信很多小伙伴都不陌生&#xff0c;在许多场景下也能看得到&#xff0c;它其实可以算是文件存储服务器&#xff0c;当然&#xff0c;你如果给它加上其他服务的话&#xff0c;它也能变成网页服务器、Office协同办公服务器等等。 有许多小伙伴都拿这…

信息安全工程师(38)防火墙类型与实现技术

一、防火墙类型 按软、硬件形式分类 软件防火墙&#xff1a;通过软件实现防火墙功能&#xff0c;通常安装在个人计算机或服务器上&#xff0c;用于保护单个设备或小型网络。硬件防火墙&#xff1a;采用专门的硬件设备来实现防火墙功能&#xff0c;通常部署在企业网络边界或数据…

基于SpringBoot图书馆预约与占座小程序【附源码】

效果如下&#xff1a; 首页界面 用户登录界面 查看座位界面 管理员登录界面 管理员主界面 座位分布信息界面 预约信息界面 研究背景 随着互联网技术的不断进步和智能手机的广泛普及&#xff0c;图书馆作为知识获取和学习的重要场所&#xff0c;其管理方式也在逐步向信息化和智…

系统架构设计师论文《论企业应用系统的数据持久层架构设计》精选试读

论文真题 数据持久层&#xff08;Data Persistence Layer&#xff09;通常位于企业应用系统的业务逻辑层和数据源层之间&#xff0c;为整个项目提供一个高层、统一、安全、并发的数据持久机制&#xff0c;完成对各种数据进行持久化的编程工作&#xff0c;并为系统业务逻辑层提…

【电路基础 · 4】电路的图;KCL、KVL巩固;支路电流法

一、电路的图 1.线性电路的一般的分析方法 2.计算方法 掌握计算方法。 3.支路 branch 和 节点 node 对于支路&#xff0c;经常取电压、电流为同向。 4.KCL 巩固 巩固一下之前学习的 KCL。 但是需要注意&#xff1a; 对于一个电路&#xff0c;如果有 n 个节点&#xff0c;那…

浅学React和JSX

用antd做个人博客卡到前端了&#xff0c;迫不得已来学react&#xff0c;也是干上全栈了-- --学自尚硅谷张天禹react React就是js框架&#xff0c;可以理解为对js做了封装&#xff0c;那么封装后的肯定用起来更方便。 相关JS库 react.js&#xff1a;React核心库。react-dom.js&a…

计算机基本组成和工作原理(Basic Components and Working Principles of Computers)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

node配置swagger

安装swagger npm install swagger-jsdoc swagger-ui-express 创建 swagger.js 配置文件 ​ const path require(path); const express require(express); const swaggerUI require(swagger-ui-express); const swaggerJsDoc require(swagger-jsdoc); // 修改 swaggerDoc…

DAY26||669.修建二叉树 |108.将有序数组转换为二叉搜索树|538.把二叉搜索树转换为累加树

669.修剪二叉树 题目&#xff1a;669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树…

ArcGIS属性表怎么连接Excel表格?

ArcGIS中&#xff0c;属性表是存储空间要素非几何特征属性的重要工具。有时&#xff0c;我们需要将这些属性与外部数据&#xff0c;如Excel表格中的数据进行连接。以下是如何在ArcGIS中实现这一过程的步骤。 要把Excel表里的数据导入到ArcGIS里的地图数据里面&#xff0c;对数…

[单master节点k8s部署]34.ingress 反向代理(一)

ingress是k8s中的标准API资源&#xff0c;作用是定义外部流量如何进入集群&#xff0c;并根据核心路由规则将流量转发到集群内的服务。 ingress和Istio工作栈中的virtual service都是基于service之上&#xff0c;更细致准确的一种流量规则。每一个pod对应的service是四层代理&…

City Builder Urban 城市都市街道建筑场景模型

目前拥有178项优质资产。 城市建设者:Urban一个高质量的资产包,专为快速的纽约式城市建设而设计,与所有渲染管道兼容。 资产 56个带LOD的街道和屋顶道具 13个可堆叠的建筑部件与LOD混合搭配 10个不同尺寸的建筑装饰/分离器,总共40个装饰 请参阅秋季列表的技术细节 1个带有C…

【redis-07】redis实现主从复制架构和底层原理

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756【三】redis缓存穿透、缓存击穿、缓存雪崩htt…

UE管理内容 —— FBX Static Mesh Pipeline

目录 General Setup Export Mesh Static Mesh LODs FBX导入流程中加入 静态网格体 支持后&#xff0c;将网格体从3D软件加入虚幻引擎的操作便极为简便&#xff1b;网格体导入后&#xff0c;网格体的材质纹理&#xff08;仅限漫反射和法线贴图&#xff09;也将被导入&#xf…

Bianchi模型、python计算及ns3验证

由于项目与学习需要,最近学习了bianchi模型,并在python中进行了公式->代码的转化,仿真结果与ns3结果对比。 本文更多的是理解模型各个部分的含义、把各个简单的推导过程转化为python、ns3对比: 1 理论吞吐与传输概率、传输成功概率、包长、速率、排队时间、成功传输时…

基于双PI控制器和SVPWM的定转子双永磁同步电机simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 永磁同步电机的基本结构和工作原理 4.2 双PI控制器的设计方法 4.3 SVPWM 的实现原理 5.完整工程文件 1.课题概述 为了实现定转子双永磁同步电机的高性能控制&#xff0c;通常采用双 PI 控制器和 S…

Linux系统创建新分区并挂载的方法

一、引言 本文以CentOS为例讲述Linux系统创建新分区并挂载的方法。如下图所示&#xff0c;用fdisk -l命令可以看到该CentOS系统下有一个磁盘/dev/vda&#xff0c;其容量为2199G&#xff0c;即2T。该磁盘有两个分区&#xff1a;vda1和vda2&#xff1a; 用lsblk命令可以查看到磁…

Ancient City Ruins 古代城市遗址废墟建筑游戏场景

这个包包含450多个古代遗迹预制件,可组合模块化预制件和许多建筑元素。它适用于室内和室外,并允许不同层次的定制建筑。 [亮点] 超过450个古代遗迹的预制件 可组合的模块化预制件 许多建筑元素 适用于室内和室外 允许不同层次的自定义建筑 预制房间和建筑 废墟装饰道具 基本自…

jQuery——循环翻页

本文分享到此结束&#xff0c;欢迎大家评论区相互讨论学习&#xff0c;下一篇继续分享jQuery中自动翻页功能的学习。