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

C++ 日期类接口实现与 const 成员函数深度解析:this 指针的只读约束

目录

一、const 成员

二、日期类的默认成员函数

三、获得某年某月的天数

四、日期加天数

【思考】:先实现operator+=,还是先实现operator+

五、日期减天数

六、前置++、--,后置++、--

七、运算符重载

八、日期减日期

九、流插入和流提取运算符重载

1、流插入运算符 operator<< 重载

2、流提取运算符 operator>> 重载


一、const 成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

由于this指针原本的类型是类名* const this,当成员函数被const修饰时 ,this的类型会从类名* const this,变为const 类名* const this,也就是说this指针this指针指向的对象都不能被修改了

我们来解决4个问题:

1、const对象可以调用非const成员函数吗?

2、非const对象可以调用const成员函数吗?

3、const成员函数内可以调用其它的非const成员函数吗?

4、非const成员函数内可以调用其它的const成员函数吗?

1、不可以,因为权限不能放大

2、可以,因为权限可以缩小

3、不可以,因为权限不能放大

4、可以,因为权限可以缩小

二、日期类的默认成员函数

class Date { public: // 全缺省的构造函数 Date(int year = 1900, int month = 1, int day = 1) { if (month > 0 && month < 13 && day > 0 && day <= GetMonthDay(year, month)) { _year = year; _month = month; _day = day; } else { cout<<"非法构造"<<endl; assert(0); } } // 拷贝构造函数 // d2(d1) Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day ; } // 赋值运算符重载 // d2 = d3 -> d2.operator=(d3) Date& operator=(const Date& d) { _year = d._year; _month = d._month; _day = d._day; return *this; } // 析构函数 ~Date() { _year = 0; _month = 0; _day = 30; } private: int _year; int _month; int _day; };

日期类的默认成员函数完全可以只写一个构造函数,因为日期类的成员变量都是内置类型,对于拷贝构造和赋值重载编译器默认生成的浅拷贝完全够用了,至于析构函数,日期类没有申请动态资源,内置类型出了作用域,编译器会自动销毁,不会涉及资源释放等问题

三、获得某年某月的天数

关于计算日期,我们最常用的就是获得某年某月天数的接口,由于日期类的成员变量是私有的,所以只能在类内部实现这个接口(声明:本章节的所有接口我都没进行声明和定义分离,你们自己私下实现的时候可以自己进行声明和定义分离

// 获取某年某月的天数 int GetMonthDay(int year, int month) { static int days[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; } return days[month]; }

days数组下标为0的位置放了个0,这点很细节,这个时候我们的月份和下标就直接可以对应上了

闰年的二月是29天,所以得单独做判断

四、日期加天数

// 日期+=天数 Date& operator+=(int day) { _day = _day + day; while (_day > GetMonthDay(_year,_month)) { _day -= GetMonthDay(_year, _month); _month++; if (_month > 12) { _year += 1; _month = 1; } } return *this; } // 日期+天数 Date operator+(int day) { Date tmp(*this); tmp += day; return tmp; }

_day先加上day,通过减去当前月的天数并增加月份来进行进位,循环此过程,直到_day的数值处于当前月的天数范围内时,停止调整,完成日期的进位计算

+操作直接复用+=即可,但是要记住不能返回引用,因为+的时候,*this指针所指向的对象是不能改变的,所以返回的是临时构造的对象,因此不能返回引用

【思考】:先实现operator+=,还是先实现operator+

从图中可以看到,先+后+=先+=后+多了一次赋值运算符重载,所以,先+=后+效率更高

五、日期减天数

这个接口的效率分析和日期加天数是没区别的,所以依旧先实现-=,在复用-=

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

先给_day减去day,只要_day在当前月天数的范围内,就不用再进行操作了,反之,只要_day<=0,就代表不符合范围,就要进行借月份加天数操作,依次循环进行操作,直到_day符合当月天数,如果_month==0了,就需要进行借年操作,同时更新年和月份

六、前置++、--,后置++、--

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

直接复用日期加减天数操作,注意一点,不能返回局部变量的引用

七、运算符重载

// >运算符重载 bool operator>(const Date& d) const { if (_year > d._year) { return true; } else if (_year == d._year && _month > d._month ) { return true; } else if(_year == d._year && _month == d._month && _day > d._day) { return true; } return false; } // ==运算符重载 bool operator==(const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; } // >=运算符重载 bool operator >= (const Date& d) const { return *this > d || *this == d; } // <运算符重载 bool operator < (const Date& d) const { return !(*this >= d); } // <=运算符重载 bool operator <= (const Date& d) const { return !(*this > d); } // !=运算符重载 bool operator != (const Date& d) const { return !(*this == d); }

只要实现了operator > 和 operator == ,别的直接复用即可

八、日期减日期

【思路1】:力求解,小的日期一直++,如果和大的日期相等了,就能计算相差天数

int Date::operator-(const Date& d) const { 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; }

缺点:虽然实现简单,但是效率低

时间复杂度:两个日期的相差天数,也就是O(N)


【思路2】:先确定两个日期的大小,让大、小日期分别减到当年1月1日,这个时候就能确定当前年差了几天,然后再计算差了几年,如果是闰年就+366,平年就+365

int Date::operator-(const Date& d) const { Date max = *this; Date min = d; int flag = 1; // 区分大小日期,修正flag if (*this < d) { max = d; min = *this; flag = -1; } int count = 0; // max减到当年1月1日 while (!(max._day == 1 && max._month == 1)) { --max; // 依赖operator--(日期减一天) ++count; } // min减到当年1月1日 while (!(min._day == 1 && min._month == 1)) { --min; // 依赖operator--(日期减一天) --count; } // 计算年份差的总天数 while (min._year != max._year) { if (is_leapyear(min._year)) { count += 366; } else { count += 365; } min._year++; // 这里假设可以直接修改成员变量,若封装严格需用接口 } return flag * count; }

时间复杂度:算当前年的天数差,最多循环365 次(因为一年最多 365 天),属于O(1),再计算年份差的总天数,差几年就是几循环次数等于年份差 Y,也属于O(1),所以时间复杂度是O(1)

九、流插入和流提取运算符重载

1、流插入运算符operator<<重载

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

作用:实现自定义类型Date向输出流(如cout)的打印


2、流提取运算符operator>>重载

istream& operator>>(istream& in, Date& d) { int year, month, day; in >> year >> month >> day; if (month > 0 && month < 13 && day > 0 && day <= d.GetMonthDay(year, month)) { d._year = year; d._month = month; d._day = day; } else { cout << "非法日期" << endl; assert(0); } return in; }

作用:实现从输入流(如cin)读取数据并赋值给Date对象,同时进行日期合法性校验。

ostream(如 cout)和 istream(如 cin)类不可拷贝,且为支持流运算符的链式调用,其重载的 operator<<和 operator>> 中对应的流参数及返回值必须用引用传递

http://www.jsqmd.com/news/483796/

相关文章:

  • PlantUML完全指南:用文本绘制专业图表的终极教程
  • 如何构建可维护的图表库:ApexCharts.js模块化架构设计完全指南
  • 如何快速上手Ambrose?5分钟搭建你的第一个数据工作流监控系统
  • 如何使用Fluent UI打造智能动态表单:条件字段显示与隐藏完全指南
  • pdfmake终极指南:5个实用技巧快速掌握JavaScript PDF生成
  • 终极音乐标签编辑指南:让您的音乐库重获新生
  • Obsidian Advanced Slides布局设计指南:网格与分栏功能全解析
  • React Markdown 终极指南:如何在React应用中安全高效地渲染Markdown内容?
  • 终极Fluent UI主题切换可访问性指南:打造人人可用的主题切换功能
  • Design OS高级技巧:10个提升设计效率的专业方法
  • PySCIPOpt实战手册:数学优化从零到精通的完整攻略
  • stack-docker脚本全解析:setup.sh自动化部署背后的秘密
  • 终极指南:5分钟掌握http-server零配置静态服务器部署
  • AICore游戏AI开发库:从零构建智能游戏角色的终极指南
  • 探索practical-nlp-code:从入门到精通的自然语言处理实战指南
  • SenseVoice-small部署教程:低配VPS(1C2G)运行ONNX量化版可行性验证
  • 为什么选择sig-storage-local-static-provisioner?5大核心优势深度剖析
  • 回顾C语言
  • 文脉定序参数详解:rerank_threshold动态阈值过滤低置信度候选结果
  • 实时交互体验升级:InternLM-XComposer2.5-OmniLive双部署方案对比(SRS Server vs Gradio)
  • 终极指南:Fluent UI组件错误边界边缘情况的10个处理策略
  • VibeVoice Python调用实战:自定义脚本集成TTS功能教程
  • SpringBoot 脚手架搭建指南:从零构建企业级开发框架
  • periph库实战案例:使用Go语言开发树莓派硬件项目
  • USBMap常见问题解答:解决你的macOS USB端口映射困惑
  • Alpamayo-R1-10B保姆级教程:WebUI日志实时监控与常见报错代码速查表
  • qmd高级技巧:如何优化你的知识库索引策略与搜索精度
  • GTE+SeqGPT轻量化部署指南:560M参数模型在消费级GPU上的高效运行方案
  • CosyVoice2-0.5B多场景落地:乡村振兴广播站、社区防疫通知方言语音生成
  • Qwen3-ForcedAligner-0.6B部署案例:单卡A10/A40离线运行,数据不出域