【C++】类和对象demo-实现日期类

文章目录

  • 实现日期类
      • 默认构造函数
        • 注意事项:
      • 求每个月的天数
      • 打印信息
    • 运算符重载:
      • +=和+运算符重载
      • ++(前置++和后置++)
      • < > <= >= == !=
      • -和-=
      • 日期-日期
      • 获取今天是星期几
      • << 流插入运算符重载
        • 总结:
      • >> 流提取运算符重载
      • Date.h
      • Date.cpp
      • Test.cpp

实现日期类

默认构造函数

如果我们不实现,编译器默认帮我们实现一份!

//构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	//判断年月日是否合法
	if (! ( (_year >= 0) && (month > 0 && month < 13) && ( day <= GetMonthDay(year,month) ) ) )
	{
		cout << "非法日期" << endl;
		Print();//this->Print();
	}
}

为了防止出现非法日期:如1月32日之类的,要写一个函数求出该月有多少天!

注意事项:

【C++】类和对象demo-实现日期类_第1张图片


求每个月的天数

每次进来都要定义这个数组,这个数组不变,可以加static修饰

//用于求每个月的天数
//int ret = d1.GetMonthDay(2022, 2); 要通过对象去调用,成员函数的第一个参数默认是this指针!
int Date::GetMonthDay(int year, int month)
{
	//每次进来都定义这个数组,所以可以放在静态区
	//大小定义为13是为了让下标和月份对应的天数对应上
	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = monthDayArray[month];
	//判断闰年
	//把月的判断放在前面,效率高!
    if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		day += 1;
	}
	return day;
}

打印信息

//打印对象的信息
//d1.Print();
void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

运算符重载:

+=和+运算符重载

如果想要知道一个日期+100天之后是什么时候?

【C++】类和对象demo-实现日期类_第2张图片

这个月不够了,往下一个月进位.注意天数要减去原来月份对应的天数

要判断是否有年进位!

//+=运算符重载
// d1+=100
Date& Date::operator+=(int day) 
{
	_day += day;//先把天数加上
	//考虑天数的进位
	//GetMonthDay(_year, _month)求出这一年的这个月有多少天
	while (_day >= GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
		++_month;//月份进1位
		if (_month == 13)
		{
			//年进位
			_month = 1;
			_year++;
		}
	}
	return *this;
}

+= 返回的是加了之后的日期,并且会改变原来的对象.

出了作用域之后,this指针销毁了,但是*this空间还在,所以可以使用传引用返回.


+的逻辑和+= 同理

//+的运算符重载
//d1+10;
Date Date::operator+(int day)
{
	//先拷贝构造一份
	Date ret(*this);
	ret += day;//调用上面的+=运算符重载
	// 相当于ret.operator+=(day)
	//返回这个对象的年月日
	return ret;
}

此时不能改变原来的对象,如:i+10,i不改变.

所以先拷贝构造一份原来的对象,然后用这个临时对象复用+=的逻辑.

出了作用域之后,这个临时对象就销毁了.所以不能使用传引用返回,只能使用传值返回!返回的仍然是一个对象


++(前置++和后置++)

【C++】类和对象demo-实现日期类_第3张图片

++ 此时只有一个操作数->无参 (因为成员函数的第一个参数默认是this指针)

++之后返回的仍然是一个对象

为了让前置++和后置++进行区分:后置++增加了占位参数,这个占位参数只能是int类型,值是多少无所谓


//前置++
Date& Date::operator++()
{
	//原对象自增,要写成+=1,日期类不能写成++
	*this += 1;
	return *this;
}

前置++:先自增,再使用值

this:调用该函数的对象的地址,*this:就是该对象.让该对象自增,然后返回

由于出了作用域,*this还在,所以可以使用传引用返回!


//后置++
Date Date:: operator++(int)
{
	//先拷贝原对象,再返回
	Date ret(*this);
	//原对象自增,要写成+=1,日期类不能写成++
	*this +=1;
	return ret;
}

后置++:先使用值,再自增

先拷贝构造一份原对象,然后对原对象自增,然后返回这个临时拷贝对象

由于临时拷贝对象出了作用域就销毁了,所以不能使用传引用返回!这个和+的运算符重载类似


后置++和+的运算符重载都会调用两次拷贝构造 (第一次:拷贝构造临时对象 第二次:传值返回)


< > <= >= == !=

//>
//d1 > d2  ->d1.operator>(&d1,d2);
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;
	}
	else
	{
		return false;
	}
}

//==
//d1==d2 ->d1.operator==(&d1,d2);
bool Date:: operator==(const Date& d)
{
	//年月日都相同才是相同的对象
	return _year == d._year
		&& _month == d._month
	    && _day == d._day;
}

//<
//d1= d);
}
//!=
//d1!=d2
bool Date:: operator!=(const Date& d)
{
	//复用 == 运算符重载函数
	return !(*this == d);
}
//>=
//d1>=d2
bool Date:: operator>=(const Date& d)
{
	return *this > d || *this == d;
}
//<=
//d1<=d2
bool Date:: operator<=(const Date& d)
{
	return *this == d || *this < d;
	//return !(*this > d);
}

上述:

我们只实现了> 和 == 的运算符重载,其它都复用了

实现< 和 == 也可以进行复用

不仅仅是日期类可以这样子,所有的类要实现比较都可以用这种方式!


-和-=

实现+和+=的时候,要进位.实现-和-=的时候 ->借位

进位的时候,要减的是当月的天数,因为当前月已经过完了

借位的时候,借的是上个月的天数.还要考虑跨年的情况->即出现0月的情况

【C++】类和对象demo-实现日期类_第4张图片


相减出来得出的_day <=0 ->不合法,要获取上个月的天数进行借位,天数相加,直到/_day >0.如果不合法就继续处理

// -=
//d1-=10;
Date& Date::operator-=(int day)
{
	_day -= day;
	//_day <= 0 就是不合法需要处理
	while (_day <= 0)
	{
		//往月借位
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		//加 上一个月的天数
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

出了作用域,*this(d1)还在,所以可以用引用返回

出了作用域:this销毁了,但是*this没有销毁


//d1-10
Date Date:: operator-(int day)
{
    //拷贝构造一份
	Date ret(*this);
    //临时对象复用-=的逻辑
	ret -= day;
    //返回临时对象 
	return ret;
}

先拷贝当前对象,然后复用-=运算符重载函数

出了作用域ret不在了,不能用引用返回!


测试自己的代码是正确:

  1. 正常的 2.边界的 3.跨年的 4.跨越闰年的

【C++】类和对象demo-实现日期类_第5张图片

-和-=复用的是一个逻辑,所以只需测试一个即可


当减一个负数的时候,会有问题!

原因:

_day -= day;
//_day 减去一个负数 -> +一个正数 不进入循环
while (_day <= 0)

解决办法:

判断一下day的值,如果是负数,就调用+=的运算符重载函数


//d1-=10;
Date& Date::operator-=(int day)
{
	//传参是负数
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	//_day <= 0 就是不合法需要处理
	while (_day <= 0)
	{
		//往月借位
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		//加 上一个月的天数
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

-不用处理,因为复用的是-=的逻辑


同理:+=也需要处理了!

//+=运算符重载
Date& Date::operator+=(int day) 
{
    //负数情况
	if (day < 0)
	{
		return *this -= -day;
	}
	_day += day;//先把天数加上
	//考虑天数的进位
	//GetMonthDay(_year, _month)求出这一年的这个月有多少天
	while (_day >= GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
		++_month;//月份进1位
		if (_month == 13)
		{
			//年进位
			_month = 1;
			_year++;
		}
	}
	return *this;
}

+=一个负值,相当于减


日期-日期

【C++】类和对象demo-实现日期类_第6张图片

日期 - 天数 ->返回值还是日期对象 日期 -日期 -> 返回值是整形

思路1:直接相减:多少年多少月多少天不好控制!月不齐(每个月的天数都不一样),还要考虑闰年平年

思路2:先比较出哪个日期更大,然后让小的日期不断++,加了多少次二者相等了,两个日期就相差多少天

先假设其中一个日期更大,令flag = 1

//日期 -日期
//offerDay - today
int Date::operator-(const Date& d)
{
    //假设offerDay更大
	Date max = *this;
	Date min = d;
	int flag = 1;
    //offerDay是小的
	if (*this < d)
	{
		max = d;
		min = *this;
        //更改flag的值
		flag = -1;
	}
	int count = 0;
	while (min != max)
	{
		++count;
		++min;
	}
    //如果offerDay是大的:flag = 1,二者之差为正数
    //如果offerDay是小的:flag = -1,二者之差为正数
    //所以是count * flag
	return count * flag;
}


获取今天是星期几

方法:找一个起始的基准值 : 如 1900年1月1日 -> 星期1

然后利用 - 运算符重载 求出目标日期和基准值的差值

7天为一周 -> 周期为7 用差值%7就是星期几 ,返回的是 0 - 6

x % n => [0,n-1],所以可以定义一个数组,让下标和星期对应.

//今天是星期几
void Date::PrintWeekDay()
{
	const char* arr[] = { "星期一","星期二" ,"星期三" ,"星期四" ,"星期五" ,"星期六" ,"星期天" };
	//使用匿名对象的方式
	//int count = *this - Date(1900, 1, 1);
	Date start(1900, 1, 1);
	int count = *this - start;
	cout << "arr[count%7]" << endl;
}

<< 流插入运算符重载

【C++】类和对象demo-实现日期类_第7张图片

【C++】类和对象demo-实现日期类_第8张图片


// << 操作符重载
void Date::operator<<(ostream& out)
{
	out << _year << "/" << _month << "/" << _day << endl;
}
【C++】类和对象demo-实现日期类_第9张图片

注意:运算符重载里面,如果是双操作数的操作符重载,第一个参数是操作数,第二个参数是右操作数

所以按上述写法:调用时写成:d1 << cout ->相当于 d1.operator<<(&d1,cout);

【C++】类和对象demo-实现日期类_第10张图片

写成成员函数的话,第一个参数默认就是this指针,第一个参数一定是我们的对象.如果想让cout在左边调用

->即 cout << d1 那就不能写在成员函数里 -> 在类外面写

void operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;//Date类的变量如果是私有的就不能在类外面进行访问
}

但是这样我们就要把成员变量改成公有的才能访问到,或者通过函数接口GetMonth…得出成员变量的值


另一种解决办法:使用友元函数->

注意:友元函数的声明要放在类里面

【C++】类和对象demo-实现日期类_第11张图片


但是这也引出了新的问题:

如果想连续输出呢?cout << d1 << d2;

此时 cout << d1 返回类型为void void不能作为左操作数


所以可以写成:

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

【C++】类和对象demo-实现日期类_第12张图片

由于流插入只是输出一下值为多少,不会改变对象的值,所以可以加const修饰对象

cout是全局对象,不会销毁

总结:

写在类成员函数时:双操作数的运算符重载时,规定第一个参数是左操作数,第二个参数是右操作数

void operator<<(ostream& out);

成员函数:默认第一个参数是默认的this指针,我们调用这个流插入重载时

d.opeartor<<(cout) <==> d<

实现成这样,是可以调用的.但是不符合使用习惯的解释含义

所以可以在全局外面写运算符重载函数,因为成员函数内:第一个参数就是默认的this指针不能改变


>> 流提取运算符重载

istream& operator>>(istream& in,Date& d)

由于要修改对象,所以对象不能加const修饰

为了支持连续的输入,要有返回值. in就是cin的别名.cin是全局对象,出了作用域不销毁

istream& operator>>(istream& in,Date& d)
{
	cout << "请依次输入年月日,以空格间隔:>" << endl;
	in >> d._year >> d._month >> d._day;
	return in;
}

【C++】类和对象demo-实现日期类_第13张图片


Date.h

#pragma once
#include
using namespace std;
class Date
{
public:
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
	//构造函数
	Date(int year = 0, int month = 1, int day = 1);
	void Print() const;
	int GetMonthDay(int year, int month);
	//两个对象进行判断的运算符重载
	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++();//前置++
	//后置++为了和前置++进行区分,增加一个参数进行占位!二者构成了函数重载
	Date operator++(int);//后置++
	Date& operator-=(int day);
	Date operator-(int day) const;
	//日期 -日期
	int operator-(const Date& d) const;
	//今天是星期几
	void PrintWeekDay() 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;
	//判断年月日是否合法
	if (! ( (_year >= 0) && (month > 0 && month < 13) && ( day <= GetMonthDay(year,month) ) ) )
	{
		cout << "非法日期" << endl;
		Print();//this->Print();
	}
}
//用于求每个月的天数
int Date::GetMonthDay(int year, int month) 
{
	//每次进来都定义这个数组,所以可以放在静态区
	//大小定义为13是为了让下标和月份对应的天数对应上
	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = monthDayArray[month];
	//判断闰年
	//把月的判断放在前面,效率高!
	if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
	{
		day += 1;
	}
	return day;
}
//打印对象的信息
void Date::Print() const
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
//+=运算符重载
Date& Date::operator+=(int day) 
{
	if (day < 0)
	{
		return *this -= -day;
	}
	_day += day;//先把天数加上
	//考虑天数的进位
	//GetMonthDay(_year, _month)求出这一年的这个月有多少天
	while (_day >= GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);//减去这个月对应的天数
		++_month;//月份进1位
		if (_month == 13)
		{
			//年进位
			_month = 1;
			_year++;
		}
	}
	return *this;
}
//+的运算符重载
Date Date::operator+(int day) const
{
	//先拷贝构造一份
	Date ret(*this);
	ret += day;//调用上面的+=运算符重载
	// 相当于ret.operator+=(day)
	//返回这个对象的年月日
	return ret;
}
// 前置++和后置++的运算符重载
//前置++
Date& Date::operator++()
{
	//原对象自增,要写成+=1,不能写成++
	*this += 1;
	return *this;
}
//后置++
Date Date:: operator++(int)
{
	//先拷贝原对象,再返回
	Date ret(*this);
	//原对象自增,要写成+=1,不能写成++
	*this +=1;
	return ret;
}
	
//d1>d2
bool Date:: operator>(const Date& d) const
{
	//先比较年
	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;
	}
	else
	{
		return false;
	}
}
//d1 ==d2
bool Date:: operator==(const Date& d) const
{
	//年月日都相同才是相同的对象
	return _year == d._year
		&& _month == d._month
	    && _day == d._day;
}
//d1!=d2
bool Date:: operator!=(const Date& d) const
{
	//复用 == 运算符重载函数
	return !(*this == d);
}
//d1= d);
}
//d1>=d2
bool Date:: operator>=(const Date& d) const
{
	return *this > d || *this == d;
}
//d1<=d2
bool Date:: operator<=(const Date& d) const
{
	return *this == d || *this < d;
	//return !(*this > d);
}

//d1-=10;
Date& Date::operator-=(int day)
{
	//防止传参是负数
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	//_day <= 0 就是不合法需要处理
	while (_day <= 0)
	{
		//往月借位
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		//加 上一个月的天数
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
Date Date:: operator-(int day) const
{
	Date ret(*this);
	ret -= day;
	return ret;
}

//日期 -日期
//offerDay - today
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;
	}
	int count = 0;
	while (min != max)
	{
		++count;
		++min;
	}
	return count * flag;
}

//今天是星期几
void Date::PrintWeekDay() const
{
	const char* arr[] = { "星期一","星期二" ,"星期三" ,"星期四" ,"星期五" ,"星期六" ,"星期天" };
	//使用匿名对象的方式
	//int count = *this - Date(1900, 1, 1);
	Date start(1900, 1, 1);
	int count = *this - start;
	cout << "arr[count%7]" << endl;
}


ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in,Date& d)
{
	cout << "请依次输入年月日,以空格间隔:>" << endl;
	in >> d._year >> d._month >> d._day;
	return in;
}

Test.cpp

#include"Date.h"
void Test1()
{
	//普通的
	Date d1(2022, 1, 18);
	Date ret1 = d1 - 10;
	ret1.Print();

	//边界
	Date ret2 = d1 - 18;
	ret2.Print();

	//跨年
	Date ret3 = d1 - 500;
	ret3.Print();

	//跨年
	Date ret4 = d1 - 1500;
	ret4.Print();
	
	//给负数的样例
	Date ret5 = d1 - -100;
	ret5.Print();
}
void Test2()
{
	Date today(2022, 1, 19);
	Date offerDay(2022, 9, 1);
	cout <<(offerDay - today) << endl;
}
void Test3()
{
	Date d1(2022, 1, 19);
	Date d2(2022, 1, 20);
	cout << d1 << d2;
}
void Test4()
{
	Date d1;
	Date d2;
	cin >> d1 >> d2;
	d1.Print();
	d2.Print();
}
int main()
{
	Test4();
	return 0;
}

你可能感兴趣的:(C++,c++,开发语言,算法,数据结构,c语言)