【C++】类和对象(十二):实现日期类

大家好,我是苏貝,本篇博客带大家了解C++的实现日期类,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • 1 ==/!=/>/</>=/<=运算符重载
  • 2 +/-/+=/-=运算符重载
    • (A) 先写+,再通过+写+=
    • (B) 先写+=,再通过+=写+
    • (C) 先写-,再通过-写-=
    • (D) 先写-=,再通过-=写-
  • 3 前置/后置++/--
  • 4 日期-日期
  • 5 >>/<<运算符重载
    • (A) <<
    • (B) >>
  • 6 优化
    • (A) const
    • (B) 判断日期是否有效
  • 7.模块化代码实现
    • 1. Date.h
    • 2. Date.cpp
    • 3. test.cpp

日期类需要自主实现构造函数,运算符重载(>,<,>=,<=,==,!=,+,-,+=,-=,++),不需要自主实现析构函数(没有动态开辟空间)、拷贝构造函数(成员变量中没有自定义类型的)。经过前面的学习,我们可以比较快的写出Date类的大部分

现在我们来新建3个文件Date.h、Date.cpp、test.cpp
将Date类放在Date.h文件中
在这里插入图片描述

上述函数的声明写在类中,但实现写在Date.cpp中

构造函数用全缺省的,注意:缺省参数如果声明和定义分离,那么缺省参数写在函数声明,函数定义中不写。因为声明和定义分离,所以函数定义时函数名前需要加Date::
在这里插入图片描述

1 ==/!=/>/</>=/<=运算符重载

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 +/-/+=/-=运算符重载

我们有2种写法,一是先写+,再通过+写+=。二是先写+=,再通过+=写+
这里的+/-是+/-天数,如2022年3月4日加10天=2022年3月14日

(A) 先写+,再通过+写+=

在实现+运算符重载前,我们要得到每个月的天数。可以定义一个函数,用if/else条件判断语句来返回天数,当然,这比较麻烦。最简单的方法是定义一个数组
在这里插入图片描述

上面的函数可以优化一下,将数组用static修饰,这样数组就不在栈区。以后就不用每调用一次GetMonthDay函数,就要定义一个数组,等到函数运行完成,还要销毁数组了。

问:下面的Date tmp=this;是拷贝构造还是赋值运算符重载?拷贝构造,因为是用已存在的类类型对象this创建新对象tmp
在这里插入图片描述
在这里插入图片描述

为什么+运算符重载返回值类型是Date,+=运算符重载返回值类型是Date&?
如果可以,我们都希望返回的是Date&类型的,因为这返回的是引用。如果返回的是Date类型的,那么返回的是返回值的拷贝。
Tmp是临时变量,临时变量出了作用域就销毁了,所以临时变量不能用引用返回,所以返回值类型是Date。下面的返回值类型同理

(B) 先写+=,再通过+=写+

在这里插入图片描述

问:上面的2种方法写+和+=,哪一种更好?

在这里插入图片描述

右边的更好,因为左边的+=里复用+需要创建对象,右边的+里复用+=不需要创建对象

© 先写-,再通过-写-=

在这里插入图片描述

(D) 先写-=,再通过-=写-

在这里插入图片描述

同理,-/-=中先写-=再通过-=写-更好

3 前置/后置++/–

在这里插入图片描述

在这里插入图片描述

4 日期-日期

这和-天数是函数重载

思路:
先知道哪个的日期较大/较小,让较小的日期进入循环,每循环一次,较小的日期+1,等到两日期相等,看循环的次数就知道两日期相差几天

在这里插入图片描述

5 >>/<<运算符重载

(A) <<

我们知道,<<(流插入运算符)支持内置类型的打印,但不支持自定义类型的打印
在这里插入图片描述

那我们想实现<<打印自定义类型怎么办?自己写一个<<的运算符重载

将<<运算符重载的声明写在类中,<<运算符重载需要有参数:cout(类型是ostream),为什么?因为<<是双操作符,因此需要2个形参,形参this隐含,所以还需要cout当实参

在这里插入图片描述
在这里插入图片描述

为什么cout<<d1;会报错?因为双操作数的操作符,左右的操作数必须和运算符重载函数的形参顺序相同,函数的第一个形参是隐藏的this,第二个才是out。因此可以d1<<cout,而不能cout<<d1。

为解决这个问题,我们就要是out是第一个形参,this是第二个形参,可是这在类中的成员函数中是不可能的。因此<<运算符重载不能写在类里,要写在全局域中。
注意:全局函数(如函数a)的定义不能写在.h文件中。如果全局函数的定义写在.h文件中,因为.h文件被2个.cpp文件包含,所以预处理时2个.cpp文件都会展开.h文件,这样就会有2个a,链接时会报错。

因此<<运算符重载也要声明和定义分离
在这里插入图片描述

Date类的成员变量是private修饰的,即不能在类外被访问。所以上图会报错,如何解决?
方法1:在Date类里写GetYear/GetMonth/GetDay函数
方法2:友元声明

友元声明即在类中也写一下函数的声明,且在声明前+friend(全局域里也需要有函数的声明,因此这个函数的声明有2个)
在这里插入图片描述

完成上面的要求,就能让<<打印自定义类型

在这里插入图片描述

在这里插入图片描述

在C++中,<<是可以连续使用的,但我们实现的不可以,为什么?
在这里插入图片描述

因为我们相等<<运算符重载没有返回cout,如果返回了cout,那么第一次cout<<d1后的返回值是cout,那接下来的表达式为cout<<d2<<endl。因此修改<<运算符重载的返回值,就能连续使用<<

在这里插入图片描述

在这里插入图片描述

(B) >>

Cin的类型是istream。>>和<<运算符重载一样,都需要在类中有友元声明

在这里插入图片描述

在这里插入图片描述

6 优化

(A) const

在Date类的成员函数中,有许多是不修改*this的,所以这些函数的this指针可以被const修饰

(B) 判断日期是否有效

像下图,2月30日是不可能的情况,所以我们需要对输入的日期进行检查
在这里插入图片描述
在这里插入图片描述

在哪里需要检查日期的有效性呢?需要输入日期的地方:构造函数和>>运算符重载

构造函数:
在这里插入图片描述

运算符>>重载:

在这里插入图片描述

7.模块化代码实现

1. Date.h

#pragma once

#include<iostream>

using std::endl;
using std::cout;
using std::cin;
using std::istream;
using std::ostream;


class Date
{
public:
	bool CheckValid();
	Date(int year = 2020, int month = 1, int day = 1);
	
	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) 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);

	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

	int GetMonthDay(int year, int month)
	{
		static int monthday[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 monthday[month];
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

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

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

2. Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	if (!CheckValid())
	{
		cout << "构造日期错误" << _year << "-" << _month << "-" << _day << 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
{
	return !(*this == d);
}

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 !(*this == d) && !(*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 tmp(*this);
	tmp += day;
	return tmp;
}

Date& Date::operator+=(int day)
{
	_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)
//{
//	Date tmp(*this);//拷贝构造
//	//Date tmp = *this;//拷贝构造
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		tmp._month++;
//		if (tmp._month == 13)
//		{
//			tmp._year++;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//}
//
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;
//	return *this;
//}

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

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

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

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

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

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

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



//d1-d2
int Date::operator-(const Date& d)
{
	Date max(*this);
	Date min(d);
	int flag = 1;
	if (min > max)
	{
		flag = -1;
		max = d;
		min = *this;
	}

	int tmp=0;
	while (min != max)//比min<max要简单
	{
		++min;
		tmp++;
	}

	return tmp * flag;
}

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


istream& operator>>(istream& in, Date& d)
{
	cout << "请依次输入年月日:";
	while (1)
	{
		in >> d._year >> d._month >> d._day;

		if (!d.CheckValid())
		{
			cout << "输入的日期错误,请重新输入:";
		}
		else
			break;
	}
	return in;
}

bool Date::CheckValid()
{
	if (_year <= 0
		|| _month > 12 || _month <= 0
		|| _day > GetMonthDay(_year, _month))
	{
		return false;
	}
	else
		return true;
}

3. test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include"Date.h"

int main()
{
	Date d1(2022, 2, 30);
	cout << d1 << endl;
	

	/*d1.operator<<(cout);
	cout << d1;
	d1 << cout;*/

	/*cin >> d1;
	cout << d1 << endl;*/

	/*d3 = d1--;
	cout << d3;

	d3 = --d2;
	cout << d3;*/

	//cin >> d3;


	/*cin >> d1 >> d2 >> d3;
	cout << d1 << d2 << d3;*/

	//cout << d1;

	/*d1.operator<<(cout);
	d1 << cout;*/

	/*cout << (d1 - d2) << endl;
	cout << (d2 - d1) << endl;*/

	/*d3 = ++d1;
	d3.Print();

	d3 = d2++;
	d3.Print();*/

	/*d3 = d1 + 49;
	d3.Print();

	d3= d1 - 342;
	d3.Print();

	d2 += 67;
	d2.Print();

	d2 -= 98;
	d2.Print();*/


	/*d2 = d1;
	cout << (d1 == d2) << endl;
	cout << (d1 != d2) << endl;
	cout << (d1 > d2) << endl;
	cout << (d1 >= d2) << endl;
	cout << (d1 < d2) << endl;
	cout << (d1 <= d2) << endl;*/

	return 0;
}

好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

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

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

相关文章

免费送源码:Java+Springboot+MySQL Springboot酒店客房管理系统的设计与实现 计算机毕业设计原创定制

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对酒店客房管理等问题&#xff0c;对酒店客房…

无线配置实验

配置 sw1 [sw1]vlan batch 10 20 100 [sw1-GigabitEthernet0/0/1]port link-type trunk [sw1-GigabitEthernet0/0/1]port trunk allow-pass vlan 10 20 100 [sw1-GigabitEthernet0/0/1]port trunk pvid vlan 100 接口g124一样的配置 [sw1-GigabitEthernet0/0/3]port link-type…

在基于AWS EC2的云端k8s环境中 搭建开发基础设施

中间件下载使用helm,这里部署的都是单机版的 aws-ebs-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:name: aws-ebs-storageclass provisioner: kubernetes.io/aws-ebs parameters:type: gp2 # 选择合适的 EBS 类型&#xff0c;如 gp2、io1…

旋转位置编码

1. Transformer为什么需要位置编码 因为 transformer 结构本身是和位置编码无关的&#xff1a; Y T ( X ) F ( A ( X ) ) Y\Tau(X)F(A(X)) YT(X)F(A(X))&#xff0c;其中 A ( ) A() A() 是 attention 变换&#xff0c;只进行了矩阵变换&#xff0c;跟位置无关&#xff0c; …

激活函数、条件熵和最大熵在机器学习的应用

文章目录 摘要Abstractsigmoid 和 softmaxsigmoid和softmax的关系 条件熵最大熵总结 摘要 本周学习内容探讨了神经网络中激活函数的选择及其对梯度消失问题的影响。通过使用 ReLU 函数替代 Sigmoid 函数来改善梯度消失问题的优化方法&#xff0c;同时分析了 Sigmoid、Softmax …

【AIGC】深入探索『后退一步』提示技巧:激发ChatGPT的智慧潜力

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;“后退一步”技巧介绍技巧目的 &#x1f4af;“后退一步”原理“后退一步”提示技巧与COT和TOT的对比实验验证 &#x1f4af;如何应用“后退一步”策略强调抽象思考引导提…

uni-app 下拉刷新、 上拉触底(列表信息)、 上滑加载(短视频) 一键搞定

一、下拉刷新 1. 首先找到pages.json中 给需要进行下拉刷新的页面设置可以下拉刷新 2. 然后在需要实现下拉刷新的script标签内添加 导入onPullDownRefresh import {onPullDownRefresh} from dcloudio/uni-app 下拉刷新触发的事件 onPullDownRefresh(()> {console.log(正…

Springboot与easypoi(2):合并单元格、二级表头、动态导出

一、纵向合并单元格 使用Excel(needMerge true)标记的属性表示此单元格需要合并。ExcelCollection表示一对多的集合&#xff0c;下面是合并单元格案例。 实体类 企业类&#xff1a; package com.ywz.entity;import cn.afterturn.easypoi.excel.annotation.Excel; import cn.…

android——渐变色

1、xml的方式实现渐变色 效果图&#xff1a; xml的代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools…

使用python画一颗圣诞树

具体效果&#xff1a; 完整代码&#xff1a; import random def print_christmas_tree(height): # 打印圣诞树的顶部 for i in range(height): # 打印空格&#xff0c;使树居中 for j in range(height - i - 1): print(" ", end"") # 打印星号&…

SAP ABAP开发学习——BAPI

目录 业务对象 概念 ​编辑业务对象浏览 BAPI BAPI的浏览 BAPI的调用 BAPI的确认和返回 BAPI的创建 MM/SD常用BAPI 附加&#xff1a;长文本修改 业务对象 概念 业务对象浏览 进入SWO3查看 双击BUS2012 双击下图上方红色位置可以看到BAPI方法的内容 BAPI BAPI(Busines…

MR30分布式IO:石化行业的智能化革新

在浩瀚的工业领域中&#xff0c;石化行业如同一座巨大的化工厂&#xff0c;将自然界的原始资源转化为人们日常生活中不可或缺的各种产品。然而&#xff0c;随着生产规模的扩大和工艺复杂度的提升&#xff0c;石化行业面临着前所未有的挑战&#xff1a;如何在保证生产效率的同时…

VLAN高级特性:VLAN聚合

一、VLAN聚合的概述 在一般的三层交换机中&#xff0c;通常是采用一个VLAN对应一个VLANIF接口实现广播域之间的互通&#xff0c;这导致了在一些情况下造成了IP地址的浪费。 因为一个VLAN对应的子网中&#xff0c;子网号&#xff0c;子网广播地址、子网网关地址不能用作VLAN内…

【Linux系列】磁盘空间不足

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

进程与线程的初探

什么是进程&#xff1f; 现代的操作系统需要运行各种各样的程序。为了管理这些程序的运行&#xff0c;操作系统提出了进程(process)的抽象&#xff1a;每一个进程对应一个运行中的程序。 进程 进程的状态 为了对进程进行管理&#xff0c;操作系统定义了进程的状态。 新生状态…

DNS服务器部署

一、正反向分析 二、主从服务器配置 前提操作&#xff1a; 虚拟机关闭防火墙 一、正反向分析 1、主配置文件 2、正向分析 3、反向分析 二、主从服务器配置 从服务器主配置 从服务器Type改为slave。 编写正反向分析文件。 测试&#xff1…

Spring DispatcherServlet详解

文章目录 Spring DispatcherServlet详解一、引言二、DispatcherServlet的初始化与工作流程1、DispatcherServlet的初始化1.1、加载配置和建立WebApplicationContext1.2、初始化策略 2、DispatcherServlet的工作流程2.1、请求分发2.2、代码示例 三、总结 Spring DispatcherServl…

计算机性能分析的三个模型

计算机性能分析的三个模型【1】 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09;二、利特尔法则&#xff08;Littles Law&#xff09;【2】三、M/M/1 QueueReference 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09; 瓶颈分析可以帮我们更好地定位导致性能…

Golang | Leetcode Golang题解之第520题检测大写字母

题目&#xff1a; 题解&#xff1a; func detectCapitalUse(word string) bool {// 若第 1 个字母为小写&#xff0c;则需额外判断第 2 个字母是否为小写if len(word) > 2 && unicode.IsLower(rune(word[0])) && unicode.IsUpper(rune(word[1])) {return f…