learn C++ NO.5 ——类和对象(3)

日期类的实现

在前面类和对象的学习中,由于知识多比较多和碎,需要一个能够将之前所学知识融会贯通的东西。下面就通过实现日期类来对类和对象已经所学的知识进行巩固。

日期类的基本功能(.h文件)

//Date.h//头文件内容
#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:

    // 获取某年某月的天数
    int GetMonthDay(int year, int month);

    // 全缺省的构造函数
    Date(int year = 2023, int month = 5, int day = 16);

    // 日期+=天数
    Date& operator+=(int day);

    // 日期+天数
    Date operator+(int day)const;

    // 日期-天数
    Date operator-(int day)const;

    // 日期-=天数
    Date& operator-=(int day);

    // 前置++
    Date& operator++();

    // 后置++
    Date operator++(int);

    // 后置--
    Date operator--(int);

    // 前置--
    Date& operator--();

    // >运算符重载
    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;

    // 日期-日期 返回天数
    int operator-(const Date& d)const;

    void Print()const
    {
        std::cout << _year << " "
            << _month << " "
            << _day << std::endl;
    }

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

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

默认构造函数的实现

由于实现的是日期类的成员对象都是内置类型,所以构造函数、析构函数、拷贝构造、赋值符重载这些都可以使用编译器默认生成的。这里我就实现一个带缺省参数的构造函数即可。

带缺省参数构造的实现

其实日期类这种都是内置类型的类并不需要我们自己写构造函数,但是为了复习与回顾知识点还是写上。采取声明定义分离的方式来实现。

//Date.h中放声明
    Date(int year = 2023, int month = 5, int day = 16);

// Date.c中实现
// 全缺省的构造函数
//Date:: 用于指明类域
Date::Date(int year, int month, int day)
{
    _year = year;
    _month = month;
    _day = day;
}

需要注意的是当声明和定义分离时,我们只能将缺省参数放在声明中,定义内就不能出现缺省参数。不然会和编译器的默认构造函数起冲突。

比较运算符重载的实现

在日常生活中不免的会对另个日期进行比较,并且在后面的模块中还会需要日期类的比较功能。下面我们就来实现一下比较运算功能。

等于运算符重载的实现

对于两个日期类等于的判断条件无非就是年、月和日都相等。

//()后的const修饰的是this指针,不改变成员变量,可以用const修饰this指针
bool Date::operator==(const Date& d)const
{
    return _year == d._year
    && _month == d._month
    && _day == d._day;
}

小于运算符重载的实现

此次比较年月日,只要比第一个操作数小,就返回真,否则返回假。

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

其他比较运算符重载的实现

其实,当完成了小于和等于运算符重载时,我们就可以复用这两个运算符重载的内容。来实现其他比较运算符的功能了。比如要实现大于运算符重载时,只需用在小于等于运算符重载的基础上进行逻辑取反即可。

//重载小于等于
bool Date::operator<=(const Date& d)const
{
    return (*this < d) || (*this == d);
}

//重载不等于
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);
}

日期的加减功能的实现

首先,我们需要明确的一点是运算符重载必须要重载有意义的运算符。所以像日期+日期这类并没有实际意义的就没必要进行运算符重载。

每月天数获取接口

在实现日期加减运算符重载难免需要频繁获取月份所对应的天数,所以这里我直接把它封装成一个接口。接口的实现思路如下:开辟静态数组存储月份对应的日期,利用数组下标与月份天数的绝对映射来判断每月的天数。当月份为二月时,判断是否为闰年,若是闰年就返回29天。将该函数用static修饰成静态成员函数可以制定类域访问该成员函数,这个在下面会有使用场景。

static int Date::GetMonthDay(int year, int month)
{
	//使用静态数组,可以避免频繁创建数组,提升效率。
    static int DaysArr[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;
    else
        return DaysArr[month];
}

日期+=天数的实现

实现思路如下:首先,日期加的天数。然后,如果日期天数大于月份天数循环让天数减去该月天数。直到天数小于等于该月天数。

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

日期+天数的实现

日期+天数,原来的日期不能变。这就是+和+=的区别。日期加天数的实现这里只需要复用一下+=即可。

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

日期-=天数和日期类-天数的实现

日期-=天数实现思路如下:先让日期类天数-=天数,此时如果日期类的天数依旧大于0,则直接返回日期类。若日期类的天数小于等于0,则让月份先–,然后判断月份有效性,最后让日期类天数+=该月份天数直至日期类天数大于0。日期类-天数的实现只需要创建临时变量并拷贝构造为*this,然后让tmp-=天数,返回tmp即可。

// 日期-=天数
Date& Date::operator-=(int 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-=(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)
{
	if(day < 0)
	{
		return *this -= (-day);
	}
    _day += day;
    while(_day > GetMonthDay(_year, _month))
    {
        _day -= GetMonthDay(_year, _month);
        _month++;
        if(_month == 13)
        {
            _month = 1;
            _year++;
        }
    }
    
    return *this;
}

前置++和后置++的实现

c++规定前置++运算符重载是无参数的,而后置++带有类型参数以示区别。前置++现将对象的自身值自增1后,返回对象。后置++先将对象的值保存到临时变量中,对象值自增1后,返回临时变量。


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

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

前置–和后置–的实现

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

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

日期-日期返回天数的实现

实现思路如下:默认左操作数为较大值,并且默认天数为负数。如果右操作数大于左操作数,则较大值为右操作符,且天数为正数。循环++较小的操作数,并记录循环次数。直到两个操作数相等。

// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
	//默认为负数
    int flag = -1;
    Date max = *this;
    Date min = d;
    if(d > *this)
    {
        max = d;
        min = *this;
        //天数为正数
        flag = 1;
    }
    int n = 0;
    while(min != max)
    {
        min++;
        n++;
    }
    return n*flag;
}

<< 和 >>运算符重载

首先,我们先了解一下cout和cin这两个类对象。
在这里插入图片描述
为什么cout << 内置类型变量,就可以自动识别内置类型变量呢?这是因为c++标准库对<<运算符进行了运算符重载。而多个内置类型的运算符重载又构成了函数重载。

#include<iostream>
using namespace std;
int main
{
	int a = 10;
	double d =20.11
	cout << a;
	cout << d;
}

而重载<<运算符,第一个参数必须是cout。而成员函数默认占用成员函数的第一个参数。所以这里这能进<<运算符重载到全局中。那么第二个问题来了,如何让全局函数突破类域的限制呢?这里就要用到友元关键字来修饰函数了。友元函数可以突破类域的限制。我们将友元函数的声明放在类的内部。这样友元函数就可以突破类域限制,访问类的私有成员变量。

    friend ostream& operator <<(ostream& out, const Date& d);
    friend istream& operator >>(istream& in, Date& d);
ostream& operator <<(ostream& out, const Date& d)
{
    out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
    return out;
}

关于不存在日期的解决问题

这里为了避免天数月份小于1,月份大于12,天数大于月数的最大天数。所以我们应该在构造函数和>>运算符重载中,对日期进行相应的判断。

Date::Date(int year, int month, int day)
{
    if((month > 12 || month < 1) ||
       (day < 1||day > GetMonthDay(year,month))
       )
    {
        cout<<"输入错误"<<endl;
        assert(false);
    }
    else
    {
        _year = year;
        _month = month;
        _day = day;
        
    }
}

istream& operator >>(istream& in, Date& d)
{
    int year,month,day;
    in >> year >> month >> day;
    
    if((month > 12 || month < 1) ||
       (day < 1||day > d.GetMonthDay(year,month))
       )
    {
        cout<<"输入错误"<<endl;
        assert(false);
    }
    else
    {
        d._year = year;
        d._month = month;
        d._day = day;
        
    }
    
    return in;
}

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

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

相关文章

【数据结构】广度优先遍历(BFS)模板及其讲解

&#x1f38a;专栏【数据结构】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【勋章】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f381;定义 &#x1f381;遍历方法 &#x1f381;根…

什么是边缘计算盒子?边缘计算盒子可以做什么?一文带你了解边缘计算云服务器 ECS

上文&#xff0c;我们已经为大家介绍了什么是边缘计算、边缘计算的诞生、以及边缘计算与CDN之间的关系&#xff0c;感兴趣的小伙伴欢迎阅读以往文章&#xff1a; 边缘计算节点是啥&#xff1f;边缘计算与CDN有什么关系&#xff1f;一文带你了解边缘计算节点BEC&#xff08;1&am…

nest笔记十一:一个完整的nestjs示例工程(nestjs_template)

概述 链接&#xff1a;nestjs_template 相关文章列表 nestjs系列笔记 示例工程说明 这个工程是我使用nestjs多个项目后&#xff0c;总结出来的模板。这是一个完整的工程&#xff0c;使用了yaml做为配置&#xff0c;使用了log4js和redis和typeorm&#xff0c;sawgger&#…

Elasticsearch 集群部署插件管理及副本分片概念介绍

Elasticsearch 集群配置版本均为8以上 安装前准备 CPU 2C 内存4G或更多 操作系统: Ubuntu20.04,Ubuntu18.04,Rocky8.X,Centos 7.X 操作系统盘50G 主机名设置规则为nodeX.qingtong.org 生产环境建议准备单独的数据磁盘主机名 #各自服务器配置自己的主机名 hostnamectl set-ho…

STM32实现基于RS485的简单的Modbus协议

背景 我这里用STM32实现&#xff0c;其实可以搬移到其他MCU&#xff0c;之前有项目使用STM32实现Modbus协议 这个场景比较正常&#xff0c;很多时候都能碰到 这里主要是Modbus和变频器通信 最常见的是使用Modbus实现传感器数据的采集&#xff0c;我记得之前用过一些传感器都…

学习RHCSA的day.03

目录 2.6 Linux系统的目录结构 2.7 目录操作命令 2.8 文件操作命令 2.6 Linux系统的目录结构 1、Linux目录结构的特点 分区加载于目录结构&#xff1a; 使用树形目录结构来组织和管理文件。整个系统只有一个位于根分区的一个根目录&#xff08;树根&#xff09;、一棵树。…

盘点 | 10大类企业管理系统有哪些

人类的发展史也是一部工具的进化史&#xff0c;企业管理手段同样不例外。移动互联网时代给了传统低下的手工操作方式致命一击&#xff0c;应运而生的各类企业管理系统工具为企业管理插上腾飞的翅膀&#xff0c;彻底颠覆了手动低效率的历史&#xff0c;变得更加移动化、智能化。…

求最小生成树(Prim算法与Kruskal算法与并查集)

目录 1、案例要求2、算法设计与实现2.1 Prim算法2.1.1 构造无向图2.1.2 编写Prim算法函数2.1.3 实现代码2.1.4 运行结果截图 2.2 Kruskal算法2.2.1 构造无向图2.2.2 编写并查集UnionFind类2.2.3 编写Kruskal算法2.2.4 实现代码2.2.5 运行结果截图 3、总结 1、案例要求 利用贪心…

低代码与其拓荒,不如颠覆开发行业

目录 一、前言 二、低代码是一个值得信赖的“黑盒子” 粗略总结&#xff0c;开发者对低代码平台所见即所得设计器有两种反应&#xff1a; 三、人人都爱黑盒子 四、用“低代码平台”来开发是什么样的感受&#xff1f; 五、结论 一、前言 在科幻电影中&#xff0c;我们看到…

【OpenCV】C++红绿灯轮廓识别+ROS话题实现

目录 前言 一、背景知识 Opencv轮廓检测 ROS相关知识 二、环境依赖 三、具体实现 Step1&#xff1a;初始化ROS&#xff0c;订阅话题 Step2&#xff1a;接收话题&#xff0c;进入回调 1. 帧处理 2. 膨胀腐蚀处理 Step3&#xff1a;红绿特征处理 1. 提取绘制轮廓 2…

【网络协议详解】——数据链路层协议(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层是 OSI 模型中的第二层&#xff0c;位于物理层之上&#xff0c;是通信网络中的重要组成部分之一。数据链路层协议负责将网络层传输的数据分组封装成帧&#xff0c;传输到物理层&#xff0c;并通过物理介质进行传输。同时&#xff0c;数…

算法笔记:A2-A4-RSRQ切换算法

1 LTE 切换 LTE切换是移动通信网络中的一个过程&#xff0c;移动设备在保持无间断服务的情况下&#xff0c;将其连接从一个基站切换到另一个基站。当移动设备离开当前基站的覆盖范围或网络资源拥塞时&#xff0c;就需要进行切换。LTE切换通常是基于特定的条件触发的&#xff0…

makefile 学习(1):C/C++ 编译过程

1. GCC 介绍 1.1 介绍 GCC 官方文档 https://gcc.gnu.org/onlinedocs/ 官方文档是最权威的&#xff0c;网上所有的答案都来自官方文档国内论坛参差不齐&#xff0c;找到好的答案比较花时间&#xff0c;并且很容易被错误的文档误导。所以推荐看官方文档靠谱点&#xff0c;并且…

二、数据字典开发

文章目录 二、数据字典开发1、搭建service-cmn模块1.1 搭建service-cmn模块1.2 修改配置1.3 启动类 2、数据字典列表2.1 数据字典列表接口2.1.1 model模块添加数据字典实体2.1.2 添加数据字典mapper2.1.4 添加数据字典controller 2.2 数据字典列表前端2.2.1 添加路由2.2.2 定义…

【Java算法题】剑指offer_01数据结构

前言 刷题链接&#xff1a; https://www.nowcoder.com/exam/oj/ta?page2&tpId13&type265 1. 链表 JZ24 反转链表 思路&#xff1a;基本操作&#xff0c;如下所示。 /* public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} }…

ad18学习笔记一

如何自学altium designer如何自学altium designer&#xff1f; - 知乎如何自学altium designer 这里面有ad官方推荐的b站的视频&#xff1a;可以直接去b站关注ad官方账号 AltiumChina&#xff0c;它本身就发布了很多实用教程。 在知乎的这个界面也有Altium Designer Ver18_官…

c++ 11标准模板(STL) std::set(六)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

如何使用SCQA模型提高表达能力

SCQA架构是“结构化表达”工具。 一、什么是“SCQA架构”&#xff1f;‍ S&#xff08;Situation&#xff09;情景——由熟悉的情境或事实引入 C&#xff08;Complication&#xff09;冲突——指出实际面临的困境或冲突 Q&#xff08;Question&#xff09;疑问——你如何分析…

文本三剑客正则表达式3

文章目录 文本三剑客&正则表达式31 awk工作原理2 awk的基本格式及其内置变量2.1 基本格式2.2 内置变量2.3 示例2.3.1 直接打印所有内容2.3.2 取每一行的第一列2.3.3 打印行号&#xff0c;及所有内容2.3.4 打印第三行2.3.5 打印2-4行2.3.6 打印第2行和第4行2.3.7 用正则表达…

基于harbor安装私有镜像仓库

目录 Harbor介绍 Harbor安装 下载完成后&#xff0c;在压缩包解压到/usr/local目录下&#xff1a; 修改Harbor配置文件 推送本地镜像到harbor上 1、给本地镜像打一个标签 2、 设置docker的daemon.json 3、重启docker 4、使用docker登录harbor 5、把本地的镜像push到harbor…