【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 << "输入的日期错误,请重新输入:";}elsebreak;}return in;
}bool Date::CheckValid()
{if (_year <= 0|| _month > 12 || _month <= 0|| _day > GetMonthDay(_year, _month)){return false;}elsereturn 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;
}
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️
相关文章:
【C++】类和对象(十二):实现日期类
大家好,我是苏貝,本篇博客带大家了解C的实现日期类,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 1 /!/>/</>/<运算符重载2 /-//-运算符重载(A) 先写,再通过写(B…...
文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《提升系统频率支撑能力的“车-氢”柔性可控负荷协同构网控制》
本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…...
异或的性质
交换两个变量的值,不使用第三个变量。 即a3,b5,交换之后a5,b3; 有两种解法, 一种用算术算法, 一种用^(异或) a a b; b a - b; a a - b; or a a^b;// 只能对int,char… b a^b; a a^b; or a ^ b ^ a; 异或交换两个变量值的方法是利用了异或运算的特性。下面是…...
新一代Webshell管理器
工具介绍 游魂是一个开源的Webshell管理器,提供更为方便的界面和更为简单易用的功能,可配合或代替其他webshell管理器,帮助用户在各类渗透场景中控制目标机器。游魂不仅支持常见的一句话webshell以及常见Webshell管理器的功能,还…...
「iOS」——知乎日报一二周总结
知乎日报仿写 前言效果Manager封装网络请求线程冲突问题下拉刷新添加网络请求的图片通过时间戳和日期格式化获取时间 总结 前言 前两周内容的仿写,主要完成了首页的仿写,进度稍慢。 效果 Manager封装网络请求 知乎日报的仿写需要频繁的申请网络请求&am…...
windows C#-匿名类型
匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。 可结合使用 new 运算符和对象初始值设定项创建匿名类型。 …...
CryptoHack 简介
CryptoHack 简介 文章目录 CryptoHack 简介一、python的安装,运行二、ASCII码三、十六进制四、Base64五、字节和大整数六、XOR1.基本2.xor属性3.xor隐藏字节4.cryptohack——You either know, XOR you dont 一、python的安装,运行 二、ASCII码 chr()函数…...
transformControls THREE.Object3D.add: object not an instance of THREE.Object3D.
把scene.add(transformControls);改为scene.add(transformControls.getHelper());...
游戏开发与游戏运营:哪个更难?
在探讨游戏产业时,游戏开发和游戏运营是两个至关重要的环节。它们各自承担着不同的职责,共同推动着游戏产品的成功与发展。然而,关于哪个环节更难的问题,并没有一个绝对的答案,因为两者都涉及复杂的流程、专业技能和独…...
大模型在自动化渗透测试中的应用
1. 引言 随着人工智能技术的快速发展,特别是大模型(如GPT-3、GPT-4等)的出现,自动化渗透测试领域迎来了新的机遇。大模型具有强大的自然语言处理能力和生成能力,能够在多个环节提升渗透测试的效率和准确性。本文将详细…...
《AI在企业战略中的关键地位:以微软和阿里为例》
内容概要 在当今商业环境中,人工智能(AI)的影响力如滔滔洪水,愈演愈烈。文章将揭示AI在企业战略中的崛起,尤其以微软和阿里巴巴为代表的企业,这两家科技巨头通过不同方式,将智能技术融入其核心…...
C语言 | Leetcode C语言题解之第537题复数乘法
题目: 题解: bool parseComplexNumber(const char * num, int * real, int * image) {char *token strtok(num, "");*real atoi(token);token strtok(NULL, "i");*image atoi(token);return true; };char * complexNumberMulti…...
Vue如何实现数据的双向绑定和局部更新?
1、Vue如何实现数据的双向绑定和局部更新? Vue.js中数据的双向绑定和局部更新可以通过v-model指令来实现。v-model是一个内置的双向数据绑定机制,用于将输入元素(如input、textarea、select等)与Vue实例的数据进行双向绑定。 在…...
java学习1
一、运算符 1.算术运算符 在代码中,如果有小数参与计算,结果有可能不精确 1-1.隐式转换和强制转换 数字进行运算时,数据类型不一样不能运算,需要转成一样的,才能运算 (1)隐式转换:…...
如何缩小PPT演示文稿的大小?
有时候PPT的磁盘空间一不小心就变得意想不到的大,比如上百MB,该如何缩小PPT的大小从而便于上传或者携带呢? 导致PPT大的原因: 媒体文件在插入或者复制到演示文稿里会被直接涵盖在其中(.pptx版本)…...
闯关leetcode——234. Palindrome Linked List
大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/palindrome-linked-list/description/ 内容 Given the head of a singly linked list, return true if it is a palindrome or false otherwise. Example 1: Input: head [1,2,2,1] Output: tru…...
通过源码分析类加载器里面可以加载的类
类列表 每一个ClassLoader里面的类列表,类的数量都是固定的。 对上一节中的dex反编译 使用DexClassLoader类动态加载插件dex 利用jadx对dex进行反编译可以看到有哪些类 源码分析 BaseDexClassLoader 从BaseDexClassLoader类加载器开始分析 在BaseDexClassLoade…...
RSA算法:数字安全的基石
## RSA算法:数字安全的基石 RSA算法是现代密码学的重要组成部分,它为安全通信和数据保护提供了坚实的基础。本文将探讨RSA算法的基本原理、实施过程以及实际应用场景。 ### 一、RSA算法概述 RSA(Rivest-Shamir-Adleman)算法是由…...
DPDK高性能处理框架VPP
VPP 环境安装 $ git clone -b stable/1801 https://github.com/FDio/vpp.git $ ./extras/vagrant/build.sh && make 在编译成功以后,会生成上图红色的 deb 表 $ dpkg –i vpp-lib_18.01.2-1~g9b554f3_amd64.deb $ dpkg –i vpp_18.01.2-1~g9b554f3_amd…...
Spring工厂方式实现实例化bean有哪些方式?
在Spring框架中,实例化Bean的方式有多种,其中通过工厂方法(Factory Method)来创建Bean是一种常见的方式。这种方式允许你通过自定义的工厂类或静态方法来生成Bean实例,从而提供了更灵活和复杂的实例化逻辑。 以下是Sp…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...

