C++——类和对象练习(日期类)

日期类

  • 1. 构造函数和析构函数
  • 2. 拷贝构造和赋值运算符重载
  • 3. 运算符重载
    • 3.1 日期的比较
    • 3.2 日期加减天数
    • 3.3 日期减日期
    • 3.4 流插入和流提取
  • 4. 取地址和const取地址重载
  • 5. 完整代码
    • Date.h
    • Date.c

对日期类进行一个完善,可以帮助我们理解六个默认成员函数,这里选择全部显式实现,包括构造函数,运算符重载,赋值重载等。对有之前的知识点的提示,以及注意事项,这里采用声明和定义分离的形式,下面代码放的全部是定义,除非必要说明。在最后会有完整的代码。包括头文件和源文件内的。

1. 构造函数和析构函数

//构造函数,初始化列表
Date::Date(int year = 2024, int month = 1, int day = 1):_year(year),_month(month),_day(day)
{}//析构函数
Date::~Date() {}

2. 拷贝构造和赋值运算符重载

//拷贝构造函数
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}//赋值运算符重载
Date& Date::operator=(const Date& d)
{//可能出现d=d的情况,即自己给自己赋值,可以不用执行代码if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}

3. 运算符重载

3.1 日期的比较

比较包括大于,等于,小于,大于或等于,小于或等于,不等于六种。

不需要全部都实现,复用代码即可。我们先写一个大于,日期的比较按照年月日依次比较,年大日期就大,年等的情况,月大日期就大,年等月等的情况,天大日期就大。剩下的所有情况就是不大于。

//运算符重载  大于
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 _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
{return !(*this > d);
}//大于等于
bool Date::operator>=(const Date& d)const
{return (*this == d) || (*this > d);
}//不等于
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}

3.2 日期加减天数

加包括 + 和 +=,减包括 - 和 -=。前者不会修改左操作数,后者会修改左操作数,比如

a=10;
a+5 //不会修改a
a+=5 //会修改a

减同理。

这里先实现 += ,我们需要判断闰年,因为闰年2月有29天,因此,这里封装一个函数,用来获得某年某月的最大天数。

//内联函数声明定义不能分离,在调用处直接展开,不用建立栈帧
inline int GetMonthDay(int year, int month) //获得某年某月的天数
{int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0){++MonthDay[2];}return MonthDay[month];
}

由于这个函数会频繁调用,因此将这个函数声明为内联函数,由于声明定义不能分离,所有这段代码是在头文件内。

有了这个函数后,在实现+=就比较容易了,我们不需要判断是不是闰年,是不是二月了,全部交给这个函数就可以了。

思路:把天全部加上,判断是否超过该月最大天数。有则进行修改,在判断是否超过最大月份,有则进行修改。循环,直到天数小于该月的最大天数。

那么+=代码如下

//日期加等天数(会改变*this)
Date& Date::operator+=(int day)
{//+=一个负数,相当于-=一个正数if (day < 0){*this -= -day;}else{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}}return *this;
}

+的实现,可以复用+=的代码

如下

//日期加天数(不会改变*this)
Date Date::operator+(int day) const
{//加不改变*this,需要创建局部变量Date retDate = *this;//拷贝构造retDate += day;//出作用域局部变量销毁,不能传引用返回return retDate;
}

因为+不能改变*this,所以先拷贝一个对象,修改这个对象的内容,在返回。

-= 和 - 的重载很类似,就不赘述了,直接放码

//日期减天数
Date Date::operator-(int day) const
{Date retDate = *this;retDate -= day;return retDate;
}Date& Date::operator-=(int day)
{if (day < 0){*this += -day;}else{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}}return *this;
}

我们的代码是否正确呢,大家可以自行测试。建议天数跨大一些,以保证不会出现特殊情况。

除了这些,还有++和–操作,并且分为前置和后置。实现起来也很容易,直接复用上面的代码即可。

//前置++,先++后使用
Date& Date::operator++()
{*this += 1;return *this;
}
//后置++,先使用后++,为了构成函数重载,后置++需要多一个int类型的参数,不需要我们传参
//函数调用时,编译器自动传参。
Date Date::operator++(int)
{//返回修改前的值Date retDate(*this);*this += 1;return retDate;
}//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
//后置--
Date Date::operator--(int)
{Date retDate(*this);*this -= 1;return retDate;
}

3.3 日期减日期

日期的相加没有意义,但是相减还是比较有意义的,比如计算一下我们从出生到现在生活了多久,和对象已经在一起多久等。

有两种方法:
法一:
在这里插入图片描述
法二:
先比较日期,在对小日期进行自增,直到与大日期相等。

这里选择法二

//日期减日期
int Date::operator-(const Date& d) const
{//找出日期大的和日期小的Date maxDate = *this > d ? *this : d;Date minDate = *this < d ? *this : d;int flag = 1;int day = 0;//第一个日期较小,结果是负数。if (minDate == *this){flag = -1;}while (minDate != maxDate){++minDate;++day;}return day * flag;
}

3.4 流插入和流提取

对于内置类型,可以使用<<和>>来打印或者从键盘输入,自定义类型也可以重载一个流插入和流提取。

// >>和<<需要重载成全局函数,因为成员函数的第一个参数默认是this,重载成成员函数会打破 cout<<d 的习惯//流插入重载
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}//流提取重载
istream& operator>>(istream& in,Date& d)
{cin >> d._year >> d._month >> d._day;return in;
}

istream和ostream是标准库内的一种输入输出类型,代码内的cou和in就是cout和cin,只不过是别名,在函数内部可以自己定义输出格式,甚至检查错误等。返回值是为了支持连续的输入和输出。

注意:在类外想使用类内部的私有成员,需要在类内部声明为友元函数。

4. 取地址和const取地址重载

这两个函数较为简单,就不过的讲解了。

//const取地址重载
const Date* Date::operator&()const
{return this;
}
//取地址重载
Date* Date::operator&()
{return this;
}

5. 完整代码

Date.h

#pragma once
#include<iostream>
using namespace std;class Date
{//友元函数声明,友元函数可以在类外使用类内的私有成员friend ostream& operator<<(ostream& out,const Date& d);friend istream& operator>>(istream& out,Date& d);public://内联函数声明定义不能分离,在调用处直接展开,不用建立栈帧inline int GetMonthDay(int year, int month) //获得某年某月的天数{int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0){++MonthDay[2];}return MonthDay[month];}//构造函数特征://              1、程序员不显示实现的话,编译器会自动生成默认构造函数//              2、函数名和类名相同,没有返回值(也不能写void,这是规定)//              3、在类对象实例化的过程中编译器对会自动调用对应的构造函数//              4、无参的构造函数,全缺省的构造函数,编译器自动生成的构造函数都可以称为默认构造函数,//                 并且默认构造函数只能存在一个,虽然编译时没有问题,但在调用时会有歧义//              5、构造函数支持函数重载//              6、对内置数据类型如int,char,double等,构造函数不做处理,//                 对自定义数据类型,编译器会调用对应的默认构造函数Date(int year = 2024, int month = 1, int day = 1);//析构函数特征://              1、函数名为类名但需要在类名前加 ~ 符号,没有参数,没有返回值(也不能写void,这是规定)//              2、我们不显示实现的话,编译器会自动生成对应的析构函数//              3、在类对象生命周期结束后,编译器自动调用对应的析构函数//              4、析构函数不能重载,因为析构函数没有参数//              5、一个类只允许有一个析构函数~Date() {};//拷贝构造函数特征://                  1、拷贝构造函数是构造函数的重载函数,所以函数名和类名相同//                  2、有且只有一个参数,该参数必须是类对象的引用,如果不是引用,编译器会直接报错//                  3、若没有显示实现,编译器会自动实现一个默认的拷贝构造函数,对已有的类对象进行拷贝//                     但是是按对应的字节序列完成拷贝,这种拷贝方式称为浅拷贝。//                  4、编译器生成的默认拷贝构造函数可以完成浅拷贝Date(const Date& d);//日期的比较,末尾const是限制*this的,此时this的类型为const Date* const this//不能修改this指针,也不能修改this指针指向的内容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=(const Date& d2);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) const;const Date* operator&()const;Date* operator&();private:int _year;int _month;int _day;
};

Date.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"//构造函数,初始化列表
Date::Date(int year = 2024, int month = 1, int day = 1):_year(year),_month(month),_day(day)
{}//析构函数
Date::~Date() {}//拷贝构造函数
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}//运算符重载  大于
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 _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
{return !(*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 retDate = *this;
//	retDate._day += day;
//	while (retDate._day > GetMonthDay(retDate._year, retDate._month))
//	{
//		retDate._day -= GetMonthDay(retDate._year, retDate._month);
//		retDate._month++;
//		if (retDate._month == 13)
//		{
//			retDate._year++;
//			retDate._month = 1;
//		}
//	}
//	
//	return retDate;
//}
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;//赋值
//	return *this;
//}//日期加天数(不会改变*this)
Date Date::operator+(int day) const
{//加不改变*this,需要创建局部变量Date retDate = *this;//拷贝构造retDate += day;//出作用域局部变量销毁,不能传引用返回return retDate;
}//日期加等天数(会改变*this)
Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;}else{_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) const
{Date retDate = *this;retDate -= day;return retDate;
}
Date& Date::operator-=(int day)
{if (day < 0){*this += -day;}else{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}}return *this;
}//前置++,先++后使用
Date& Date::operator++()
{*this += 1;return *this;
}
//后置++,先使用后++,为了构成函数重载,后置++需要多一个int类型的参数,不需要我们传参
//函数调用时,编译器自动传参。
Date Date::operator++(int)
{Date retDate(*this);*this += 1;return retDate;
}//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
//后置--
Date Date::operator--(int)
{Date retDate(*this);*this -= 1;return retDate;
}//日期减日期
int Date::operator-(const Date& d) const
{//找出日期大的和日期小的Date maxDate = *this > d ? *this : d;Date minDate = *this < d ? *this : d;int flag = 1;int day = 0;//小日期减大日期为负数if (minDate == *this){flag = -1;}while (minDate != maxDate){++minDate;++day;}return day * flag;
}// >>和<<需要重载成全局函数,因为成员函数的第一个参数默认是this,重载成成员函数会打破 cout<<d 的习惯
//流插入重载
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}//流提取重载
istream& operator>>(istream& in,Date& d)
{cin >> d._year >> d._month >> d._day;return in;
}//赋值运算符重载
Date& Date::operator=(const Date& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}const Date* Date::operator&()const
{return this;
}Date* Date::operator&()
{return this;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【黑马头条】-day12项目部署和发布-jenkins

文章目录 1 持续集成2 软件开发模式2.1 瀑布模式2.2 敏捷开发2.2.1 迭代开发2.2.2 增量开发 3 Jenkins3.1 Jenkins安装3.1.1 导入镜像3.1.2 配置3.1.3 初始化设置 3.2 插件安装3.3 服务器环境准备3.3.1 Docker安装配置3.3.2 Git安装配置3.3.3 Maven安装配置 3.4 Jenkins工具配置…

盲人过马路安全:科技力量赋予“隐形守护者”

作为一名资深记者&#xff0c;我始终关注着社会各群体的生活现状&#xff0c;尤其是那些面临特殊挑战的人群。今天&#xff0c;我想聚焦一个看似平常却对盲人构成重大困扰的日常场景——过马路&#xff0c;以及一款名为蝙蝠避障的辅助应用如何成为他们的盲人过马路安全的守护者…

Docker 部署 WordPress 并完成建站

什么是 WordPress WordPress 是使用 PHP 语言开发的博客平台&#xff0c;用户可以在支持 PHP 和 MySQL 数据库的服务器上架设属于自己的网站。也可以把 WordPress 当作一个内容管理系统&#xff08;CMS&#xff09;来使用。WordPress 是一款个人博客系统&#xff0c;并逐步演化…

【Linux系统编程】第七弹---权限管理操作(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、修改文件权限的做法(一) 2、有无权限的表现 总结 上一弹我们讲解了Linux权限概念相关的知识&#xff0c;但是我们只知道有…

【C++学习】STL之空间配置器之一级空间配置器

文章目录 &#x1f4ca;什么是空间配置器✈STL 提供六大组件的了解&#x1f440;为什么需要空间配置器&#x1f44d;SGI-STL空间配置器实现原理&#x1f302;一级空间配置器的实现 &#x1f4ca;什么是空间配置器 空间配置器&#xff0c;顾名思义就是为各个容器高效的管理空间…

python爬虫 - 爬取html中的script数据(从zum.com爬取新闻信息 )

文章目录 1. 分析页面内容数据格式2. 使用re.findall方法&#xff0c;编写爬虫代码3. 使用re.search 方法&#xff0c;编写爬虫代码 1. 分析页面内容数据格式 &#xff08;1&#xff09;打开 https://zum.com/ &#xff08;2&#xff09;按F12&#xff08;或 在网页上右键 --…

Tomcat启动闪退疑难排解全攻略:资深技术视角详解(详细)

Tomcat 启动闪退问题是在尝试启动Apache Tomcat服务器时&#xff0c;遇到的一种情况&#xff0c;其中服务器进程无法正常运行并立即退出。这个问题可能是由于多种原因造成的&#xff0c;包括配置错误、端口冲突、类加载问题等。作为一个资深技术人员&#xff0c;解决这类问题需…

【Java网络编程】TCP通信(Socket 与 ServerSocket)和UDP通信的三种数据传输方式

目录 1、TCP通信 1.1、Socket 和 ServerSocket 1.3、TCP通信示例 2、UDP的三种通信&#xff08;数据传输&#xff09;方式 1、TCP通信 TCP通信协议是一种可靠的网络协议&#xff0c;它在通信的两端各建立一个Socket对象 通信之前要保证连接已经建立&#xff08;注意TCP是一…

PostgreSQL中的索引类型有哪些,以及何时应选择不同类型的索引?

文章目录 索引 解决方案和示例代码 PostgreSQL提供了多种索引类型&#xff0c;每种类型都有其特定的应用场景和优势。选择合适的索引类型可以显著提高查询性能&#xff0c;减少数据库负载。 索引 以下是PostgreSQL中常见的索引类型及其适用场景&#xff1a; 1. B-tree 索引 …

BFS解决FloodFill算法:(Leetcode:733. 图像渲染)

题目链接&#xff1a;733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 使用广度优先遍历算法解决该问题&#xff1a; 从初始位置开始搜索&#xff0c;初始位置符合条件就入栈&#xff0c;并修改初始位置值。初始位置出栈。 再从初始位置开始广度优先搜索&#xff08;…

成都直播产业园「天府锋巢」电商流量深度变现,助力企业降本增效

天府锋巢园区环境 天府锋巢直播基地 其他重点特色产业服务 等您来解锁&#xff01; 「锋巢资讯 聚焦天府 诚邀企业 敬请关注」

【wsl】安装nvm配置

安装nvm 参考https://github.com/nvm-sh/nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bashexport NVM_DIR"$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm