首页 > 其他分享 >日期类的实现(类和对象的应用)

日期类的实现(类和对象的应用)

时间:2025-01-06 20:28:50浏览次数:8  
标签:const 对象 operator month int 日期 应用 Date day


前言

通过日期类的实行可以帮助我们更好的了解掌握之前类和对象学的6个默认成员函数(尤其是赋值运算符重载和普通对象和const对象的理解)


一、日期类的实现

  • 私有成员部分:

_year、_month、_day分别用来存储日期的年、月、日信息。

  • 成员函数部分:

static int GetMonthDay(int year, int month);
GetMonthDay函数用于判断当前月份是否为闰年,依据闰年的判断规则(能被 4 整除但不能被 100 整除,或者能被 400整除);并且根据月份对应的天数以及是否闰年等情况判断日的取值

构造函数,析构函数,拷贝构造,赋值构造函数,打印函数
还有一系列重载的成员函数

  • 构造函数部分:

全缺省构造函数可以根据传入的年、月、日值来初始化日期对象,同时会调用assert来确保传入的日期是合法的,若不合法程序会报错终止。

  • 运算符重载部分:
  1. 重载了 << 和 >> 运算符,使得可以方便地将日期对象输出 or 输入到输出流(比如cin,cout)中,输出 or 输入格式为年-月-日。
  2. 重载了+和-运算符,分别实现了日期对象增加或减少指定天数的功能,在运算过程中会根据每个月的天数以及是否闰年等情况,对日期进行合理的调整,确保最终得到的结果是合法的日期。
  3. 重载了 ++ 和 - - 运算符
  4. 重载了< <= > >= == != 运算符

二、具体实现

1. 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 = 2024, int month = 1, int day = 1);

	//拷贝构造函数
	//d2(d1) 
	//Date(const Date& d) 
	//{
	//	cout << "Date(const Date& d)" << endl;
	//	this->_year = d._year;
	//	this->_month = d._month;
	//	this->_day = d._day;
	//}

	//赋值构造函数
	//Date& operator=(const Date& d)
	//{
	//	if (this != &d)
	//	{
	//		_year = d._year;
	//		_month = d._month;
	//		_day = d._day;
	//	}
	//	return *this;//*this出了作用域还在,可以用引用返回
	//}

	void print(/*Date* this*/) const
	{
		cout << _year << "年" << _month << "月" << _day << "日"<< endl;
	}

	static int GetMonthDay(int year, int month);
	
	//重载这两个下面都可以复用
	bool operator<(const Date& x)const;
	bool operator==(const Date& x)const;

	bool operator<=(const Date& x)const;
	bool operator>(const Date& x)const;
	bool operator>=(const Date& x)const;
	bool operator!=(const Date& x)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);
	/*C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递
	编译器自动传递*/

	//日期类相减
	int operator-(const Date& d)const;


	//void operator<<(ostream& out);
	// 流插入不能写成成员函数
	// 因为Date对象默认占用第一个参数,就是做了左操作数
	// 写出来就一定是下面这样子,不符合使用习惯
	//d1 << cout; // d1.operator<<(cout); 
	
	//java一般这样使用让全局函数获取成员变量
	/*int Getyear()
	{
		return _year;
	}*/

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

//流插入运算符重载
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

2. Date.cpp

代码如下(示例):

#include"Date.h"

Date::Date(int year, int month, int day)
{
	if (month > 0 && month <= 12
		&& day > 0 && day <= GetMonthDay(year, month))//还没有初始化不用_day
	{
	this->_year = year;
	this->_month = month;
	this->_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
		assert(false);
	}
}

  int Date::GetMonthDay(int year, int month)
{
	//频繁调用,太浪费,直接放在静态区里
	static int monthArray[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;
	}
	return monthArray[month];
}


bool Date::operator==(const Date& x)const
{
	return _year == x._year && _month == x._month && _day == x._day;
}
           复用即可
bool Date::operator!=(const Date& x)const
{
	return !(*this == x);
}

bool Date:: operator<(const Date& x)const
{
	if (_year < x._year)
	{
		return true;
	}
	else if (_year == x._year && _month < x._month)
	{
		return true;
	}
	else if (_year == x._year && _month == x._month && _day < x._day)
	{
		return true;
	}
	return false;
}

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


bool Date::operator>(const Date& x)const
{
	复用即可
	return !(*this <= x);
}

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

Date& Date::operator+=(int day)//返回值是引用,支持连续赋值,*this出了作用域不销毁
{
	if (day < 0)
	{
		return *this -= -day;
	}
	this->_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month > 12)
		{
			_month = 1;
			_year++;
		}
	}
	return *this;	
}

//+复用+=合适,不会多次拷贝构造,如果+=复用+会多调用2次拷贝构造效率低下
Date Date::operator+(int day)const 
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

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

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

Date& Date::operator++()//前置++
{
	*this += 1;
	return *this;
}
//增加这个int参数不是为了具体的值,(int i)如果参数无意义可以不写,只是为了占位,区分前置++和后置++
//跟前置++构成重载
Date Date::operator++(int)//后置++
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}

//运算符重载是为了让类的对象(自定义类型)像内置类型一样使用,但是不能改变内置类型的优先级和结合性
//函数重载是支持函数名相同,但是参数列表不同的情况下,可以定义多个同名函数

//两个重载的运算符可以构成函数重载??? ---可以
//他们两个本身也是普通函数,只是他们的函数名比较特殊

//对于内置类型,前置++和后置++效率一样
//对于自定义类型,前置++效率高于后置++,因为后置++多了2次拷贝构造,传值返回还要创建一个对象

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

}
Date Date::operator--(int i)//参数可写可不写,因为参数没有实际意义
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}

//日期相减
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)
	{
		++min;
		++count;
	}
	return count * flag;
}

//流插入 or 流提取运算符重载不能写在类里,因为默认左操作数是this指针,而流插入运算符左操作数是cout,所以不能写在类里
//void Date::operator<<(ostream& out)
//{
//	out << _year << "-" << _month << "-" << _day << endl;
//}
 
// 写成全局函数
//流插入运算符重载
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}

istream& operator>>( istream& in, Date& d)//要提取他的内容也要改他的状态值
{
	int year, month, day;
	in >> year >> month >> day;	

	if (month > 0 && month <= 12
		&& day > 0 && day <= Date::GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "日期非法" << endl;
		assert(false);
	}
	return in;
}

3. test.cpp

代码如下(示例):

#include"Date.h"

void test1()
{
	Date d1(2024, 1, 1);
	d1 += 100;
	d1.print();

	Date d2(2024, 1, 1);
	d2 + 100;
	d2.print();
	Date d3 = d2 + 100;
	d3.print();

	//用一个已经存在的对象初始化另一个对象 --- 构造函数
	Date d4 = d2;/*等价于Date d4(d2); --拷贝构造*/

	//已经存在的两个对象之间复制拷贝 --- 运算符重载函数
	d4 = d1; /*等价于d4.operator=(d1); --赋值构造*/



	//+=用引用返回为了支持连续赋值
	int i = 0;
	int j = 0;
	j += i += 10;
}

void test2()
{
	Date d1(2024, 1, 1);

	d1++;//d1.operator++(&d1,0);0和1是编译器固定传的;
	d1.print();

	++d1;//d1.operator++(&d1);
	d1.print();

	//对于内置类型,前置++和后置++效率一样
	//对于自定义类型,前置++效率高于后置++
}
void test3()
{
	Date d1(2024, 1, 1);
	Date d2(2024, 1, 1);

	d1 < d2;//内置类型直接转换成指令,编译器知道怎么处理
	//编译完后,直接转换成对应的指令cmp这种

	int i = 1, j = 2;
	i < j;
}
void test4()
{
	Date d1(2023, 5, 5);
	//d1 - 30;
	d1.print();
	Date d2 = d1 - 50;
	d2.print();

}
void test5()
{
	Date d1(2025, 1, 1);
	Date d2(1964, 10, 1);

	cout << d1 - d2 << endl;
	cout << d2 - d1 << endl;
}
void test6()
{
	Date d1(2025, 1, 1);
	Date d2(1964, 10, 1);
	//流插入
	cout << d1;//operator<<(cout,d1);
	cout << d1 << d2;//operator<<(operator<<(cout,d1),d2);

	//d1 << cout;//有些类似于cout.operator<<(d1);别扭的不行

	Date d3(2025, 1, 1);
	Date d4(2025, 1, 6);

	cout << d4 << d3 << d1 << endl;//把输入的东西插入到终端

	Date d1;
	Date d2;
	cin >> d1 >> d2;//从终端输入的东西提取出来
	d1.print();
	d2.print();
}
void test7()
{
	Date d1(2025, 1, 6);
	d1.print();
	const Date d2(2025, 1, 7); 
	d2.print();

	//成员函数后面加const,修饰*this
	//普通对象和const对象都可以调
	// 注:
	//但是要修改的对象成员变量的函数不能加
	//只要成员函数内部不修改成员变量,都应该加const
	//Eg:operator+=

	//this->_day += day;

	//d1 < d2//这里因为d2的对象是const所以能过,反过来就不行了
	//d2 < d1
}
int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	test6();
	test7();
	return 0;
}

标签:const,对象,operator,month,int,日期,应用,Date,day
From: https://blog.csdn.net/2301_79262802/article/details/144967275

相关文章

  • 面向对象分析与设计Python版 面向对象思维
    文章目录一、面向对象思想的起源二、面向对象的基本概念三、面向对象的思考方式一、面向对象思想的起源软件人才软件人才从低到高4个成长层次:软件蓝领,软件工程师,卓越软件人才,领军人物卓越软件人才要求系统分析和设计理论基础,掌握大系统需求分析、建模与仿真技......
  • [读书日志]从零开始学习Chisel 第五篇:Scala面向对象编程——类继承(敏捷硬件开发语言Ch
    3.3类继承3.3.1Scala中的类继承为了节省代码量和反映实际各种类之间的联系,通常采取两种策略,包含和继承。包含是说明一个类中包含另一个类的对象,但两者之间没有必然联系。继承是从一个宽泛的类派生出更具体的类的过程,被继承的类称为“超类”或“父类”,而派生出来的类称为......
  • [读书日志]从零开始学习Chisel 第四篇:Scala面向对象编程——操作符即方法(敏捷硬件开发
    3.2操作符即方法3.2.1操作符在Scala中的解释在其它语言中,定义了一些基本的类型,但这些类型并不是我们在面向对象中所说的类。比如说1,这是一个int类型常量,但不能说它是int类型的对象。针对这些数据类型,存在一些基本操作符,比如算数操作符“+”。Scala所追求的是极致的面向对......
  • [读书日志]从零开始学习Chisel 第三篇:Scala面向对象编程——类和对象(敏捷硬件开发语言
    3.Scala面向对象编程3.1类和对象3.1.1类类是用class开头的代码定义,定义完成后可以用new+类名的方式构造一个对象,对象的类型是这个类。类中定义的var和val类型变量称为字段,用def定义的函数称为方法。字段也称为实例变量,因为每个被构造出来的对象都有自己的字段,但所有的对象公......
  • Uno Platform是一个基于C#开源、功能强大、灵活的跨平台开发框架,用于快速构建单一代码
    UnoPlatform是一个基于C#开源、功能强大、灵活的跨平台开发框架,用于快速构建单一代码库原生移动、Web、桌面和嵌入式应用程序思维导航前言项目介绍项目特点与优势主题样式风格项目源代码对应平台运行效果项目源码地址优秀项目和框架精选DotNetGuide技术社区前言......
  • 多样化消息通知样式,帮助应用提升日活跃度
    在智能手机时代,用户的通知栏充斥着各种应用推送的消息。如何在这些信息中脱颖而出,激发用户的兴趣,引导他们进一步探索,是提高应用的日活跃度(DAU)的关键。HarmonyOSSDK推送服务(Pushkit)提供了多样化的通知消息样式,开发者们可以根据应用的特点和用户的需求来定制更有创意的消息文案,吸......
  • 微信小程序应用echarts
    1、clone官方库echarts-for-weixin到本地,将其中的ec-canvas目录复制到自己项目的某个目录下(如components)2、在自己的组件或页面中(.json文件),按如下方式使用,路径根据自己的项目具体设置{"usingComponents":{"ec-canvas":"../components/ec-canvas/ec-canvas"}}3......
  • .NET Core GC对象 分配(GC Alloc)底层原理浅谈
    对象分配策略.NET程序的对象是由CLR控制并分配在托管堆中,如果是你,会如何设计一个内存分配策略呢?按需分配,要多少分配多少,移动alloc_ptr指针即可,没有任何浪费。缺点是每次都要向OS申请内存,效率低预留缓冲区,降低了向OS申请内存的频次。但在多线程情况下,alloc_ptr锁竞争会非常......
  • 10. C++对象模型和this指针
    1. 成员变量和成员函数分开存储在C++中,类内的成员变量和成员函数分开存储只有非静态成员变量才属于类的对象.classPerson{public:Person(){mA=0;}//非静态成员变量占对象空间intmA;//静态成员变量不占对象空间staticintmB;//函数也不占对象空间,所有函数共......
  • “双碳”目标下资源环境中的可计算一般均衡(CGE)模型实践技术应用
    为了实现这一目标就必须应用各种二氧化碳排放量很高技术的替代技术,不仅需要考虑技术上的可靠性,也需要考虑经济上的可行性。可计算一般均衡模型(CGE模型)由于其能够模拟宏观经济系统运行和价格调节机制,分析政策工具的影响和效应而备受“双碳”目标研究者的青睐。由于CGE模型基于严......