C++——类和对象练习(日期类)

news2024/12/28 19:00:39

日期类

  • 1. 构造函数和析构函数
  • 2. 拷贝构造和赋值运算符重载
  • 3. 运算符重载
    • 3.1 日期的比较
    • 3.2 日期加减天数
    • 3.3 日期减日期
    • 3.4 流插入和流提取
  • 4. 取地址和const取地址重载
  • 5. 完整代码
    • Date.h
    • Date.c

对日期类进行一个完善,可以帮助我们理解六个默认成员函数,这里选择全部显式实现,包括构造函数,运算符重载,赋值重载等。对有之前的知识点的提示,以及注意事项,这里采用声明和定义分离的形式,下面代码放的全部是定义,除非必要说明。在最后会有完整的代码。包括头文件和源文件内的。

1. 构造函数和析构函数

//构造函数,初始化列表
Date::Date(int year = 2024, int month = 1, int day = 1)
	:_year(year)
	,_month(month)
	,_day(day)
{}

//析构函数
Date::~Date() {}

2. 拷贝构造和赋值运算符重载

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

//赋值运算符重载
Date& Date::operator=(const Date& d)
{
    //可能出现d=d的情况,即自己给自己赋值,可以不用执行代码
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}

3. 运算符重载

3.1 日期的比较

比较包括大于,等于,小于,大于或等于,小于或等于,不等于六种。

不需要全部都实现,复用代码即可。我们先写一个大于,日期的比较按照年月日依次比较,年大日期就大,年等的情况,月大日期就大,年等月等的情况,天大日期就大。剩下的所有情况就是不大于。

//运算符重载  大于
bool Date::operator>(const Date& d)const
{
	//年大日期就大
	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		//年等,月大日期就大
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			//年等月等,天大日期就大
			if (_day > d._day)
			{
				return true;
			}
		}
	}
	return false;
}

再写一个等于,年月日都相等,日期才相等。

//等于
bool Date::operator==(const Date& d)const
{
	return _year == d._year &&
		_month == d._month &&
		_day == d._day;
}

剩下的四种比较全部复用这两个函数即可。
小于就是不大于或不等于。
小于等于就是不大于
大于等于就是不小于
不等于就是等于取反

这种方法适用于所有需要重载这六种运算符的类,只需要写一个大于和等于或小于和等于,剩下的都复用代码即可。

//小于
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) || (*this > d);
}

//不等于
bool Date::operator!=(const Date& d)const
{
	return !(*this == d);
}

3.2 日期加减天数

加包括 + 和 +=,减包括 - 和 -=。前者不会修改左操作数,后者会修改左操作数,比如

a=10;
a+5 //不会修改a
a+=5 //会修改a

减同理。

这里先实现 += ,我们需要判断闰年,因为闰年2月有29天,因此,这里封装一个函数,用来获得某年某月的最大天数。

//内联函数声明定义不能分离,在调用处直接展开,不用建立栈帧
inline int GetMonthDay(int year, int month) //获得某年某月的天数
{
	int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
	{
		++MonthDay[2];
	}
	return MonthDay[month];
}

由于这个函数会频繁调用,因此将这个函数声明为内联函数,由于声明定义不能分离,所有这段代码是在头文件内。

有了这个函数后,在实现+=就比较容易了,我们不需要判断是不是闰年,是不是二月了,全部交给这个函数就可以了。

思路:把天全部加上,判断是否超过该月最大天数。有则进行修改,在判断是否超过最大月份,有则进行修改。循环,直到天数小于该月的最大天数。

那么+=代码如下

//日期加等天数(会改变*this)
Date& Date::operator+=(int day)
{
    //+=一个负数,相当于-=一个正数
	if (day < 0)
	{
		*this -= -day;
	}
	else
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month == 13)
			{
				_year++;
				_month = 1;
			}
		}
	}
	return *this;
}

+的实现,可以复用+=的代码

如下

//日期加天数(不会改变*this)
Date Date::operator+(int day) const
{
	//加不改变*this,需要创建局部变量
	Date retDate = *this;//拷贝构造
	retDate += day;
	//出作用域局部变量销毁,不能传引用返回
	return retDate;
}

因为+不能改变*this,所以先拷贝一个对象,修改这个对象的内容,在返回。

-= 和 - 的重载很类似,就不赘述了,直接放码

//日期减天数
Date Date::operator-(int day) const
{
	Date retDate = *this;
	retDate -= day;
	return retDate;
}

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

我们的代码是否正确呢,大家可以自行测试。建议天数跨大一些,以保证不会出现特殊情况。

除了这些,还有++和–操作,并且分为前置和后置。实现起来也很容易,直接复用上面的代码即可。

//前置++,先++后使用
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
//后置++,先使用后++,为了构成函数重载,后置++需要多一个int类型的参数,不需要我们传参
//函数调用时,编译器自动传参。
Date Date::operator++(int)
{
    //返回修改前的值
	Date retDate(*this);
	*this += 1;
	return retDate;
}

//前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
//后置--
Date Date::operator--(int)
{
	Date retDate(*this);
	*this -= 1;
	return retDate;
}

3.3 日期减日期

日期的相加没有意义,但是相减还是比较有意义的,比如计算一下我们从出生到现在生活了多久,和对象已经在一起多久等。

有两种方法:
法一:
在这里插入图片描述
法二:
先比较日期,在对小日期进行自增,直到与大日期相等。

这里选择法二

//日期减日期
int Date::operator-(const Date& d) const
{
	//找出日期大的和日期小的
	Date maxDate = *this > d ? *this : d;
	Date minDate = *this < d ? *this : d;
	int flag = 1;
	int day = 0;
	//第一个日期较小,结果是负数。
	if (minDate == *this)
	{
		flag = -1;
	}
	while (minDate != maxDate)
	{
		++minDate;
		++day;
	}
	return day * flag;
}

3.4 流插入和流提取

对于内置类型,可以使用<<和>>来打印或者从键盘输入,自定义类型也可以重载一个流插入和流提取。

// >>和<<需要重载成全局函数,因为成员函数的第一个参数默认是this,重载成成员函数会打破 cout<<d 的习惯

//流插入重载
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

//流提取重载
istream& operator>>(istream& in,Date& d)
{
	cin >> d._year >> d._month >> d._day;
	return in;
}

istream和ostream是标准库内的一种输入输出类型,代码内的cou和in就是cout和cin,只不过是别名,在函数内部可以自己定义输出格式,甚至检查错误等。返回值是为了支持连续的输入和输出。

注意:在类外想使用类内部的私有成员,需要在类内部声明为友元函数。

4. 取地址和const取地址重载

这两个函数较为简单,就不过的讲解了。

//const取地址重载
const Date* Date::operator&()const
{
	return this;
}
//取地址重载
Date* Date::operator&()
{
	return this;
}

5. 完整代码

Date.h

#pragma once
#include<iostream>
using namespace std;

class Date
{
	//友元函数声明,友元函数可以在类外使用类内的私有成员
	
	friend ostream& operator<<(ostream& out,const Date& d);
	friend istream& operator>>(istream& out,Date& d);

public:
	//内联函数声明定义不能分离,在调用处直接展开,不用建立栈帧
	inline int GetMonthDay(int year, int month) //获得某年某月的天数
	{
		int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
		{
			++MonthDay[2];
		}
		return MonthDay[month];
	}

	//构造函数特征:
	//              1、程序员不显示实现的话,编译器会自动生成默认构造函数
	//              2、函数名和类名相同,没有返回值(也不能写void,这是规定)
	//              3、在类对象实例化的过程中编译器对会自动调用对应的构造函数
	//              4、无参的构造函数,全缺省的构造函数,编译器自动生成的构造函数都可以称为默认构造函数,
	//                 并且默认构造函数只能存在一个,虽然编译时没有问题,但在调用时会有歧义
	//              5、构造函数支持函数重载
	//              6、对内置数据类型如int,char,double等,构造函数不做处理,
	//                 对自定义数据类型,编译器会调用对应的默认构造函数
	Date(int year = 2024, int month = 1, int day = 1);


	//析构函数特征:
	//              1、函数名为类名但需要在类名前加 ~ 符号,没有参数,没有返回值(也不能写void,这是规定)
	//              2、我们不显示实现的话,编译器会自动生成对应的析构函数
	//              3、在类对象生命周期结束后,编译器自动调用对应的析构函数
	//              4、析构函数不能重载,因为析构函数没有参数
	//              5、一个类只允许有一个析构函数
	~Date() {};


	//拷贝构造函数特征:
	//                  1、拷贝构造函数是构造函数的重载函数,所以函数名和类名相同
	//                  2、有且只有一个参数,该参数必须是类对象的引用,如果不是引用,编译器会直接报错
	//                  3、若没有显示实现,编译器会自动实现一个默认的拷贝构造函数,对已有的类对象进行拷贝
	//                     但是是按对应的字节序列完成拷贝,这种拷贝方式称为浅拷贝。
	//                  4、编译器生成的默认拷贝构造函数可以完成浅拷贝

	Date(const Date& d);


	//日期的比较,末尾const是限制*this的,此时this的类型为const Date* const this
	//不能修改this指针,也不能修改this指针指向的内容

	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=(const Date& d2);
	Date operator+(int day)const;
	Date& operator+=(int day);

	Date operator-(int day) const;
	Date& operator-=(int day);

	Date& operator++();
	Date operator++(int);

	Date& operator--();
	Date operator--(int);

	int operator-(const Date& d) const;

	const Date* operator&()const;

	Date* operator&();

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

Date.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"

//构造函数,初始化列表
Date::Date(int year = 2024, int month = 1, int day = 1)
	:_year(year)
	,_month(month)
	,_day(day)
{}

//析构函数
Date::~Date() {}


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

//运算符重载  大于
bool Date::operator>(const Date& d)const
{
	//年大日期就大
	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		//年等,月大日期就大
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			//年等月等,天大日期就大
			if (_day > d._day)
			{
				return true;
			}
		}
	}
	return false;
}

//等于
bool Date::operator==(const Date& d)const
{
	return _year == d._year &&
		_month == d._month &&
		_day == d._day;
}

//小于
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) || (*this > d);
}

//不等于
bool Date::operator!=(const Date& d)const
{
	return !(*this == d);
}


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

//日期加天数(不会改变*this)
Date Date::operator+(int day) const
{
	//加不改变*this,需要创建局部变量
	Date retDate = *this;//拷贝构造
	retDate += day;
	//出作用域局部变量销毁,不能传引用返回
	return retDate;
}

//日期加等天数(会改变*this)
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		*this -= -day;
	}
	else
	{
		_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) const
{
	Date retDate = *this;
	retDate -= day;
	return retDate;
}
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		*this += -day;
	}
	else
	{
		_day -= day;
		while (_day <= 0)
		{
			--_month;
			if (_month == 0)
			{
				--_year;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
	}
	return *this;
}

//前置++,先++后使用
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
//后置++,先使用后++,为了构成函数重载,后置++需要多一个int类型的参数,不需要我们传参
//函数调用时,编译器自动传参。
Date Date::operator++(int)
{
	Date retDate(*this);
	*this += 1;
	return retDate;
}

//前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
//后置--
Date Date::operator--(int)
{
	Date retDate(*this);
	*this -= 1;
	return retDate;
}

//日期减日期
int Date::operator-(const Date& d) const
{
	//找出日期大的和日期小的
	Date maxDate = *this > d ? *this : d;
	Date minDate = *this < d ? *this : d;
	int flag = 1;
	int day = 0;
	//小日期减大日期为负数
	if (minDate == *this)
	{
		flag = -1;
	}
	while (minDate != maxDate)
	{
		++minDate;
		++day;
	}
	return day * flag;
}

// >>和<<需要重载成全局函数,因为成员函数的第一个参数默认是this,重载成成员函数会打破 cout<<d 的习惯
//流插入重载
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

//流提取重载
istream& operator>>(istream& in,Date& d)
{
	cin >> d._year >> d._month >> d._day;
	return in;
}

//赋值运算符重载
Date& Date::operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}

const Date* Date::operator&()const
{
	return this;
}

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

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

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

相关文章

30V-STM32设计项目

30V-STM32设计 一、项目描述 (已验证) 基于STM32c8t6芯片设计的开发板&#xff0c;支持4-30V宽电压输入&#xff0c;串口模式自动下载功能&#xff0c;支持串口和STlink&#xff0c;方式下载程序 二、原理图介绍 电源电路采用了DCDCLDO电路&#xff0c;如果是外接DC头供电的话&…

机器学习之sklearn基础教程

目录 前言 一、安装 Sklearn 二、导入 Sklearn 三、加载数据集 四、划分数据集 五、数据预处理 六、选择模型并训练 七、模型评估 八、实验不同的模型 九、调整模型参数 十、实例&#xff1a;使用Sklearn库来进行鸢尾花&#xff08;Iris&#xff09;数据集的分类 #s…

微信小程序 讯飞录音 点击按钮录音内容转文字

<page-meta page-style"{{ showPolish ? overflow: hidden; : }}" /> <view class"wrap"> <view class"header-tab" style"justify-content: {{typeList.length > 2 ? start : center}}"><view class&quo…

加速大数据分析:Apache Kylin使用心得与最佳实践详解

Apache Kylin 是一个开源的分布式分析引擎&#xff0c;提供了Hadoop之上的SQL接口和多维分析&#xff08;OLAP&#xff09;能力以支持大规模数据。它擅长处理互联网级别的超大规模数据集&#xff0c;并能够进行亚秒级的查询响应时间。Kylin 的主要使用场景包括大数据分析、交互…

【基础算法】双指针

1.移动零 移动零 思路&#xff1a; 利用双指针算法 cur&#xff1a;从左往右扫描数组&#xff0c;遍历数组 dest&#xff1a;处理好的区间包括dest dest初始化为-1&#xff0c;因为刚开始dest前应该没有非零元素。 即将非零元素移到dest之前即可 class Solution { public…

BFS解决FloodFill算法:(Leetcode:733. 图像渲染)

题目链接&#xff1a;733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 使用广度优先遍历算法解决该问题&#xff1a; 从初始位置开始搜索&#xff0c;初始位置符合条件就入栈&#xff0c;并修改初始位置值。初始位置出栈。 再从初始位置开始广度优先搜索&#xff08;…

【机器学习300问】78、都有哪些神经网络的初始化参数方法?

在训练神经网络时&#xff0c;权重初始化是确保良好收敛的关键步骤之一。不合适的初始化方法可能会导致梯度消失或爆炸&#xff0c;特别是在深层网络中。那么都有哪些神经网络的初始化参数方法呢&#xff1f;选择它这些方法的原则是什么&#xff1f; 一、常用神经网络初始化参…

Kubernetes(k8s)的概念以及使用

k8s的概念&#xff1a; K8s是指Kubernetes&#xff0c;是一个开源的容器编排和管理平台。它最初由Google开发&#xff0c;并于2014年将其开源。Kubernetes旨在简化容器化应用程序的部署、扩展和管理。 Kubernetes提供了一种可靠且可扩展的平台&#xff0c;用于管理容器化应用…

怎样才能迅速了解一个产品的业务流程?

很多小伙伴经常问我&#xff0c;刚进入一家新的企业&#xff0c;想要快速了解产品的业务流程&#xff0c;不知从何下手。主要是因为&#xff0c;有的企业根本没有文档可看&#xff1b;还有的企业有文档&#xff0c;但是记录的比较凌乱&#xff0c;想要从中找出点头绪来&#xf…

【Python-装饰器】

Python-装饰器 ■ 简介■ 装饰器的一般写法&#xff08;闭包写法&#xff09;■ 装饰器的语法 (outer写法) ■ 简介 装饰器其实是一种闭包&#xff0c; 功能就是在不破坏目标函数原有的代码和功能的前提下为目标函数增加新功能。 ■ 装饰器的一般写法&#xff08;闭包写法&am…

2024年前端技术发展趋势

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

程客有话说05 | 吕时有:在GIS行业深耕13年,做梦做出来了数学竞赛题,这是让我最开心的事

《程客有话说》是我们最新推出的一个访谈栏目&#xff0c;邀请了一些国内外有趣的程序员来分享他们的经验、观点与成长故事&#xff0c;我们尝试建立一个程序员交流与学习的平台&#xff0c;也欢迎大家推荐朋友或自己来参加我们的节目&#xff0c;一起加油。 本期我们邀请的程…

使用Docker搭建本地Nexus私有仓库

0-1开始Java语言编程之路 一、Ubuntu下Java语言环境搭建 二、Ubuntu下Docker环境安装 三、使用Docker搭建本地Nexus Maven私有仓库 四、Ubuntu下使用VisualStudioCode进行Java开发 你需要Nexus Java应用编译构建的一种主流方式就是通过Maven, Maven可以很方便的管理Java应用的…

网盘兼职真的能月入过万吗?你适合做哪种网盘分享牛?

1. 分享大容量文件&#xff1a; 提供常见软件安装包、系统镜像、游戏资源等常用的大容量文件&#xff0c;以满足用户的需求。 创建分类目录&#xff0c;便于用户浏览和查找所需文件。 编写详细的文件描述&#xff0c;包括文件版本、适用系统、安装方法等信息&#xff0c;帮助用…

Promise.all 的方法还没执行完就执行了.then

碰见一个问题&#xff0c;接盘了一个有问题的页面修改。 改变日期后 查询很多数据再去重新加载页面上的数据显示相关的组件。 问题就来了。 加载异常捏…… 最后我一通查&#xff1a; 重点来了 是因为这个Promise.all(数组)&#xff0c;里边这个数组的问题。现在是在数据中…

XYCTF 部分wp及学习记录

1.ezmd5 根据题目提示 我们知道应该是要上传两张md5值相同的图片 根据原文链接&#xff1a;cryptanalysis - Are there two known strings which have the same MD5 hash value? - Cryptography Stack Exchange 把保存下来的图片上传一下 得到flag 2.ezhttp 根据原文链接&…

STM32H7的LCD控制学习和应用

STM32H7的LCD控制 LTDC基础硬件框图LTDC时钟源选择LTDC的时序配置LTDC背景层、图层1、图层2和Alpha混合LTDC的水平消隐和垂直消隐LCD的DE同步模式和HV同步模式的区别区分FPS帧率和刷新率避免LTDC刷新撕裂感的解决方法 驱动示例分配栈的大小MPU和Cache配置初始化SDRAM初始化LCD应…

鸿蒙 harmonyos 线程 并发 总结 async promise Taskpool woker(三)多线程并发 Worker

Worker Worker是与主线程并行的独立线程。创建Worker的线程称之为宿主线程&#xff0c;Worker自身的线程称之为Worker线程。创建Worker传入的url文件在Worker线程中执行&#xff0c;可以处理耗时操作但不可以直接操作UI。 Worker主要作用是为应用程序提供一个多线程的运行环境…

办公设备租赁行业内卷瞎扯

办公设备租赁行业内卷瞎扯 最近听到很多同行抱怨&#xff0c;现在市场太卷了&#xff0c;真的有点到了卷不死就往死里卷的节奏&#xff0c;让大家都开始想换地方&#xff0c;或者转行。但是今天&#xff0c;我想从另外一个角度聊一下这个问题&#xff0c;分析一下&#xff0c;…

苍穹外卖day9 (1)用户端历史订单

文章目录 前言用户端历史订单1. 查询历史订单1.1 业务规则1.2 接口设计1.3 代码实现 2. 查询历史订单详情2.1 接口设计2.2 代码实现 3. 取消订单3.1 业务规则3.2 接口设计3.3 代码设计 4. 再来一单4.1 业务规则4.2 接口设计4.3 代码实现 前言 用户端对历史订单的操作&#xff…