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

news/2024/12/2 17:40:36/

日期类的实现

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

日期类的基本功能(.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;elsereturn 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;elsereturn 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.11cout << 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.ppmy.cn/news/74155.html

相关文章

擎创动态 | 来自华为的深度认可,擎创再获华为鲲鹏技术认证

在数字中国的信息技术应用国产化进程中&#xff0c;擎创科技除持续投入自主产品研发外&#xff0c;还深度适配了涵盖芯片、服务器、操作系统、数据库、中间件、云服务、应用等领域的国产化产品&#xff0c;与华为的合作适配便是其中重要的一环。近期&#xff0c;擎创夏洛克智能…

五分钟学会Playwright录制脚本的方法以及语法难点

这篇文章系统地介绍了上手Playwright的方法&#xff0c;但是录制脚本部分讲解不够详尽&#xff0c;今天我在这里重点的介绍一下Playwright 录制脚本的方法来丰满我的Playwright系列技术文章。 Playwright可以使用codegen来录制脚本&#xff0c;使用方式非常简单&#xff0c;只…

torch、torch.nn、 torch.optim、torchvision、 torchvision.transforms 功能简介

1. torch 库 import torch 命令将 PyTorch 框架导入到 Python 程序中&#xff0c;这样我们就可以使用 PyTorch 提供的各种功能了。PyTorch 是一个用于科学计算的机器学习库&#xff0c;具有以下重要功能&#xff1a; 张量(Tensor)操作&#xff1a;PyTorch 引入了张量作为其核心…

Istio 微服务架构的演变

微服务架构的演变 单体模式下面一个应用通常会有一个app server&#xff0c;这个app server里面会有不同的子模块&#xff0c;每一个模块都写在同一个应用包里面&#xff0c;模块和模块之间的边界有些时候设计的不是特别清晰&#xff0c;特别早期代码混合在一起那么意味着互相的…

2023-05-19 题目

1、java的三大特性&#xff0c;亦或者四大特性 继承&#xff1a;继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类&#xff08;超类、基类&#xff09;&#xff1b;得到继 承信息的类被称为子类&#xff08;派生类&#xff09;。继承让变化中的软件系统…

【微信小程序】富文本rich-text的图片预览效果的几种方法

前言 使用原生小程序开发&#xff0c;实现在富文本rich-text中的图片预览效果的几种方法对比。 update:因为方案3wxparser后续没有再维护&#xff0c;解析微信公众号文章时会出现排版错误的问题。作为插件也很难二次开发。换成mp-html了 1.正则wx.previewImage&#xff08;有…

【面试题】如何实现vue虚拟列表,纵享丝滑

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 最近在工作中遇到了一个列表的需求&#xff0c;因为做的是C端&#xff0c;所以对性能…

无效数据大揭秘——你不知道的那些坑!

进行数据管理时&#xff0c;无效数据可能会对生产力和决策质量造成严重的影响。如何发现和处理无效数据变得愈发重要。一起来唠唠各位大佬是如何处理的&#xff1f; ⭐ 什么是无效数据&#xff1f;⭐ 如何处理无效数据&#xff1f;⭐ 如何减少无效数据&#xff1f;⭐ 无效数据管…