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

news2024/11/23 16:34:24

日期类的实现

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

日期类的基本功能(.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.coloradmin.cn/o/549972.html

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

相关文章

makefile 学习(4): makefile基础

0. 官方文档 GNU Make 官方网站: https://www.gnu.org/software/makeGNU Make 官方文档下载地址: https://www.gnu.org/software/make/manual/Makefile Tutorial:https://makefiletutorial.com/ 1.基本要求 1.1 基本格式 targets : prerequisties [tab键] command target : …

一、MongoDB简介

文章目录 一、MongoDB简介1、NoSQL简介2、什么是MongoDB ?3、MongoDB 特点4、安装mongodb5、MongoDB 概念解析5.1 数据库5.2 文档5.3 集合5.4 MongoDB 数据类型 6、适用场景 一、MongoDB简介 1、NoSQL简介 NoSQL(NoSQL Not Only SQL)&#xff0c;意即反SQL运动&#xff0c;…

关于在spyder,jupyter notebook下创建虚拟环境(pytorch,tensorflow)均有效

anaconda下载地址 https://www.anaconda.com/download/ 下载完成后打开anaconda目录下的 anaconda prompt 在命令行中输入下面的命令创建一个叫tf2.0的虚拟环境&#xff08;“tf2.0”是建立的Conda虚拟环境的名字&#xff0c;可以自拟&#xff09; conda create -n tf2.0 p…

chatgpt赋能Python-pythonfor遍历

Python for 遍历&#xff1a;优雅地遍历数据结构 对于任何编程语言来说&#xff0c;遍历是一项基本操作。而在 Python 中&#xff0c;遍历是一项非常简单和优雅的操作。Python 提供了多种遍历数据结构的方法&#xff0c;包括 for 循环、while 循环、迭代器和生成器等。本文将介…

模板和STL【C++初阶】

目录 一、前言 二、函数模板 三、类模板 四、STL 一、前言 以前我们写swap函数时&#xff0c;对每一种类型的变量都要写一份swap函数&#xff0c;但是他们的格式都是一样的&#xff0c;未免有些麻烦 因此&#xff0c;我们今天学习的模板就可以针对广泛的类型而不是具体的类…

chatgpt赋能Python-pythondir

Python dir命令&#xff1a;探索Python模块的秘密 如果你是一名Python开发者&#xff0c;那么你一定或多或少接触过dir这个命令。但是&#xff0c;你了解dir到底能做什么吗&#xff1f;这篇文章将会介绍dir命令的用途、用法以及一些有趣的技巧。 什么是dir命令 简单来说&…

chatgpt赋能Python-pythonfind

Python文件搜索工具Pythonfind 在开发过程中&#xff0c;文件搜索工具是一个非常重要的工具。在大型项目中&#xff0c;可能需要查找特定类型的文件或者在代码库中查找特定的代码块。 Pythonfind是一个非常强大和灵活的python文件搜索工具&#xff0c;可以帮助我们简化这个过程…

chatgpt赋能Python-pythonforelse

Python For Else 详解&#xff1a;用 Python 的人都应该了解的语法结构 在 Python 中&#xff0c;一个常见的语法结构是 for...else。这种语法结构让循环变得更加直接明了&#xff0c;也让代码更加易读和易懂。 什么是 Python For Else 在 Python 中&#xff0c;for...else …

第13章_约束

第13章_约束 1. 约束(constraint)概述 1.1 为什么需要约束 数据完整性&#xff08;Data Integrity&#xff09;是指数据的精确性&#xff08;Accuracy&#xff09;和可靠性&#xff08;Reliability&#xff09;。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的…

chatgpt赋能Python-pythondataframe转置

Python Dataframe 转置 - 了解 Pandas 中 DataFrame 转置的用法 数据分析是现代业务中的一个关键课题。当您在数据分析中使用 Pandas 时&#xff0c;往往会遇到需要转置 DataFrame 的情况。本文中&#xff0c;我们将介绍如何使用 Python 的 Pandas 库中的 DataFrame 转置来实现…

chatgpt赋能Python-pythondoc

PythonDoc&#xff1a;了解Python的文档工具 什么是PythonDoc&#xff1f; PythonDoc是Python官方文档。它是Python编程语言的权威指南和参考资料&#xff0c;提供丰富而全面的信息&#xff0c;从基础语法到高级主题&#xff0c;都有许多实用和详细的文档说明。 PythonDoc的…

chatgpt赋能Python-pythonddos

PythonDDoS&#xff1a;了解一下这种利用Python语言的攻击方式 PythonDDoS&#xff08;Python分布式拒绝服务攻击&#xff09;是一种利用Python语言编写的DDoS攻击技术&#xff0c;它利用了Python的强大处理能力&#xff0c;可以构建高效的攻击工具&#xff0c;让攻击者能够轻…

SpringMVC学习总结(路由映射、参数传递、转发和重定向...)

目录 1. MVC简介 2. SpringMVC简介 3. 路由映射注解 3.1 RequestMapping 3.2 GetMapping与PostMapping 4. 接收前端传递参数 4.1 接收单/多个参数 4.2 接收对象 4.3 接收JSON对象 4.4 后端参数重命名/映射 4.5 设置参数必传/非必传 4.6 获取URL中的参数 4.7 获取文…

学生考勤信息管理系统

系列文章 任务36 学生考勤信息管理系统 文章目录 系列文章一、实践目的与要求1、目的2、要求 二、课题任务三、总体设计1.存储结构及数据类型定义2.程序结构3.所实现的功能函数4、程序流程图 四、小组成员及分工五、 测试操作页面bk.txt信息录入&#xff1a;加入新出勤的信息查…

chatgpt赋能Python-pythonget

PythonGet&#xff1a;一个优秀的Python包管理器 PythonGet是一个优秀的Python包管理器&#xff0c;它可以帮助Python工程师安装、管理和更新Python包。本文将在介绍PythonGet的基本用法的同时&#xff0c;探讨PythonGet在SEO优化中的应用。 PythonGet的简介 PythonGet是Pyt…

不用魔法,快速、手摸手上线Midjourney!【附源码】【示例】

首先来一波感谢&#xff1a; 感谢laf提供赞助&#xff0c;目前可以免费使用Midjourney进行开发和测试。 感谢白夜、米开朗基杨sealos.io的耐心解答&#xff0c;让我对laf有了更多的使用与了解。 什么是laf&#xff1f;来了解下。 文末有【示例】 开始 废话不多说&#xff0c;…

基于Freertos的ESP-IDF开发——7.WS2812B彩色灯循环

基于Freertos的ESP-IDF开发——7.WS2812B彩色灯循环 0. 前言1. WS2812B简介2. 完整代码3. 演示效果4. 其他FreeRtos文章 0. 前言 本节使用WS2812B实现彩灯循环 开发环境&#xff1a;ESP-IDF 4.3 操作系统&#xff1a;Windows10 专业版 开发板&#xff1a;自制的ESP32-WROOM-3…

【软考中级】软件设计师选择题题集(一)

海明校验码是在n个数据位之外增设k个校验位,从而形成一个k+n位的新的码字, 使新的码字的码距比较均匀地拉大。n与k的关系是(1)。 (1)A.2k - 1≥n + k  B.2n - 1≤ n + k   C.n = k  D.n-1≤k 【答案】A 【解析】 【答案】B A 【解析】 在采用结构化方法进行系统分析时,…

MySQL高级篇——索引失效的11种情况

导航&#xff1a; 【黑马Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线设计模式牛客面试题 目录 1. 索引优化思路 2. 索引失效的11种情况 2.0. 数据准备 2.1 要尽量满足全值匹配 2.2 要满足最佳左前缀法则 2.3 主键插…

详解Jetpack Compose中的Modifier修饰符

前言 本文将会介绍Jetpack Compose中的Modifier。在谷歌官方文档中它的描述是这么一句话&#xff1a;Modifier元素是一个有序、不可变的集合&#xff0c;它可以往Jetpack Compose UI元素中添加修饰或者各种行为。例如&#xff0c;背景、填充和单击事件监听器装饰或添加行为到文…