当前位置: 首页 > news >正文

附:日期类Date的实现

ʕ • ᴥ • ʔ

づ♡ど

 🎉 欢迎点赞支持🎉

个人主页:励志不掉头发的内向程序员;

专栏主页:C++语言;


文章目录

前言

一、日期类的框架

二、构造函数

三、析构函数

四、拷贝构造

五、运算符重载

1、operator+=(int day)

2、operator+(int day)

3、operator-=(int day)

4、operator-(int day

5、operator-(const Date& d)

6、比较运算符

7、operator++/--

8、operator<>

总结


    前言

    我们本章节主要就是使用之前的学的的C++知识来制作一个日期类,这个代码的制作可以很好的让我们加深对类和对象的理解,让我们一起来看看吧。


    一、日期类的框架

    我们类的基本框架比较简单

    class Date
    {
    public:private:int _year;int _month;int _day;
    };

    这就是我们的日期类的基本框架,里面包含了三个成员变量,我们在此框架中一点一点的填充内容以实现类的各项功能。

    二、构造函数

    	Date::Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}

    我们的构造函数,也是默认构造函数,因为不用传参。

    三、析构函数

    	Date::~Date(){_year = _month = _day = 0;}

    我们从我们的构造函数就可以看出来,我们其实是没有创建什么对象去指向自己的资源的,都是内置类型的函数,所以我们其实完全就没有必要写析构函数,但是为了代码的完整性,这里还是决定写一写。

    四、拷贝构造

    	Date::Date(const Date& d){_year = d._year;_month = d._month;_day = d._month;}

    拷贝构造同理,我们也没有必要实现。注意我们拷贝构造的传参时要传引用,不然就会产生无穷递归。

    五、运算符重载

    我们运算符重载得对有意义的运算符进行重载,那哪些运算符有意义呢

    	bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);

    首先就是比较符是有意义的,因为可以比价哪一天比哪一天晚等。

    	Date operator+(int day);Date& operator+=(int day);Date& operator-(const Date& d);Date& operator-=(const Date& d);Date operator-(int day);Date& operator-=(int day);

    其次就是这些加减运算符,像加几天是什么日期,减几天是什么日期,什么日期到日期之间有多少天等。

    我们来逐一实现

    1、operator+=(int day)

    我们如果想要加几天,那我们就的考虑那个月有多少天的问题,因为如果天加到了超过了那一个月的上限,那就得到下一个月去了,所以我们最好创建一个数组去记录我们的每个月有多少天。

    	int GetMonthDay(int year, int month){// 防止月份有误assert(month > 0 && month < 13);// 因为数组得经常使用,使用变成静态数组// 数组第0位是-1方便代码操作,因为月份数就对应数组下标了static int monthDayArray[13] = { -1, 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 monthDayArray[month];}}

    此时当我们拥有了这个数组,我们就可以知道天数超过多少就得向月份进位了。此时我们再来实现这个+的功能就简单很多了。

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

    这串代码也是比较好理解的,就是如果我们的天数大于那个月的天数最大值了我们就向月份进一后减去那个月份的天数,如果月份到达13了就向年份进一,一直到我们的天数不超过那一月的最大值循环就退出了。

    2、operator+(int day)

    我们在已经实现了+=的情况下,我们可以利用我们的+=来实现我们的+,这种操作叫做函数复用

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

    同时我们发现,我们的这两个+全是传引用返回的,这是因为这样可以减少几次拷贝构造来提高效率。

    3、operator-=(int day)

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

    我们可以把我们的天数先减成负数后再进行减少月份来把天数加成正数。

    4、operator-(int day)

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

    5、operator-(const Date& d)

    这个的主要作用就是看看我们的日期之间总共相差多少天,但是如果我们就是日期相减,虽然也可以,但是代码比较复杂,我们可以换一个思路,那就是找到我们的比较小的日期,然后再拿我们的小日期一直++,知道我们的小日期和大日期相等,我们拿个变量记录加的次数,这样就可以轻松实现了。而且这些操作我们之前就有了,只需要函数复用即可了。

    int Date::operator-(const Date& d)
    {Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
    }

    6、比较运算符

    我们一次性来看看我们的比较运算符,因为这个比较简单,我们就直接看代码吧

    <:

    bool Date::operator<(const Date& d)
    {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)
    {return _year == d._year && _month == d._month && _day == d._day;
    }
    

    <=:

    
    bool Date::operator<=(const Date& d)
    {// 复用<和==return *this < d || *this == d;
    }
    

    >:

    
    bool Date::operator>(const Date& d)
    {// 复用<=return !(*this <= d);
    }
    

    >=:

    bool Date::operator>=(const Date& d)
    {// 复用<return !(*this < d);
    }

    !=:

    bool Date::operator!=(const Date& d)
    {// 复用==return !(*this == d);
    }

    我们这个地方使用了大量的函数复用,大家可以认真观察。

    7、operator++/--

    我们都知道,我们的++/--有前置++/--和后置++/--,但是它们都是同一个operator函数,而且它们都是一元的,那我们怎么区分呢,虽然在上一章节就有提到过,但是没有仔细说明,我们这里借助我们的Date类来说明一下。

    我们这里的解决办法是我们的后置++/--在前置++/--的基础上增加一个形参int(只需要增加一个int去区分就行,不用写形参名字),这样就可以和前置++/--构成重载了,方便区分。为什么是后置++/--增加形参那是因为前置++/--经常使用,它比后置++/--少几次拷贝构造,可以传引用返回,但是后置++/--不行,我们来看看它们的代码。

    //d++
    Date Date::operator++(int)
    {Date tmp = *this;*this += 1;return tmp;
    }//++d
    Date& Date::operator++()
    {return *this += 1;
    }//d--
    Date Date::operator--(int)
    {Date tmp = *this;*this -= 1;return tmp;
    }//--d
    Date& Date::operator--()
    {return *this -= 1;
    }

    我们可以看到我们的后置++/--因为要先使用后++/--,所以要创建一个临时变量去给我们的程序去使用,这样就会多几次拷贝构造。

    8、operator<</>>

    我们的类对象如果想要使用流输入/输出也是需要进行重载的,cin/cout只支持内置类型。所以我们来看看我们怎么重载流输入和输出,我们的流输入(cin)和输出(cout)的返回值分别对应的是istream/ostream当然用void也可以简单替代。首先我们先看看我们输出的简单一点的函数形式

    void Date::operator<<(ostream& out);

    这是我们写在类内时的写法,我们可能学的好的会发现问题,那就是我们调用时是这样调用的

    cout << *****;

    按道理说参数应该先是ostream,再是Date啊,但是类内默认this指针是第一啊。确实是这样,所以如果这样写我们就只能这样调用

    d.operator<<(cout);
    // 或者
    d << cout

    这样简直倒反天罡,为了避免这种事情发生,我们必须想办法让这两个参数位置调换,所以我们不能让我们的<<重载变成成员函数,得把它写出来变成全局函数

    ostream operator<<(ostream out, const Date& d)
    {out << d._year << "日" << d._month << "月" << d._day << "日" << endl;
    }

    这样写我们就可以把上面的位置调换过来,但是又产生了一个新的问题那就是我们访问不了私有了,也就是上面的那种形式是会报错的,我们之前讲解了如何使我们的外界函数访问私有,在这里在说明一个,那就是友元函数,但是友元函数细讲是在后面,这里只是说一下怎么用。

    只要在我们想要访问的类中加一个函数的友元声明我们便可在此函数中访问该类的私有

    class Date
    {// 友元声明friend void Func();
    public:
    private:
    }void Func()
    {
    }

    友元声明就是函数声明前加一个friend即可,可以加在类的任意位置,这样我们的Func即可访问类中的私有了。

    此时还有一个问题,那就是不能连续的输出我们的数据

    cout << *** << *** << ....;

    我们上面的函数是无法做到这一点的,原因是因为我们的返回值是void而非ostream,我们的输出输入操作符是从左往右结合,与其他运算符相反,也就是我们cout和下一个数据结合后返回值是void,这样就不能继续结合了,但是如果返回值是ostream,那便可以继续往后结合,所以我们最终的写法应该是

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

    我们的输入也是相同的写法,大家自己去试试吧。

    注:我们istream和ostream必须是传引用,因为它们的拷贝构造被封了,无法调用,所以必须引用,不然会报错。


    总结

    以上就是我们日期类的写法,希望大家能够对我们的类的理解更加的深入。

    🎇坚持到这里已经很厉害啦,辛苦啦🎇

    ʕ • ᴥ • ʔ

    づ♡ど

    http://www.dtcms.com/a/331501.html

    相关文章:

  • Pytorch在FSDP模型中使用EMA
  • Leetcode_1780.判断一个数字是否可以表示成三的幂的和
  • UE5 C++ 删除文件
  • BotCash:GPT-5发布观察 工程优化的进步,还是技术突破的瓶颈?
  • Spring Boot + Redis Cluster 测试
  • 回流(Reflow)与重绘(Repaint):浏览器渲染性能优化核心
  • 演员念真主演《镇恶追凶》辽宁杀青
  • 数字电路上的通讯速度是越快越好还是越慢越好?
  • 【二分图】染色问题
  • 企业智脑UMI AIGC SaaS:解锁AI时代全场景生产力,中小微企业转型利器
  • Linux学习-多任务(进程)
  • **隐私沙盒:发散创新之光**随着互联网技术的飞速发展,数据安全和隐私保护逐渐成为人们关注的焦点。隐私沙盒作为一种新兴
  • Ping32 与绿盾再对比:Ping32 以创新与适配领跑数据安全​
  • 机器学习内容总结
  • 机器学习-基础入门:从概念到核心方法论
  • MySQL进阶——优化、日志
  • 第4节课:多模态大模型的核心能力(多模态大模型基础教程)
  • 疏老师-python训练营-Day45Tensorboard使用介绍
  • StarRocks优化统计分析
  • 好用的开源数据可视化设计工具LIGHT CHASER
  • Java List 集合详解(ArrayList、LinkedList、Vector)
  • pyecharts可视化图表-pie:从入门到精通
  • 适用工业分选和工业应用的高光谱相机有哪些?什么品牌比较好?
  • 这个就是哈希冲突
  • AI出题人给出的Java后端面经(十四)(日更)
  • 智慧养老解决方案:破解“最后一公里”服务难题
  • 【98页PPT】智慧方案某著名企业汽配行业ERP整体解决方案(附下载方式)
  • BGP笔记及实验
  • 网络层协议——IP
  • 2025年机器视觉与信号处理国际会议(MVSP 2025)