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

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


🚀 个人主页:< 脏脏a-CSDN博客 >

📊 文章专栏:< C++ >

🔗 上篇回顾:<【中篇】类和对象 >

📋 其他专栏:< Linux > 、< 数据结构 > 、< 优选算法 >

目录

一、const成员

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

三、获得某年某月的天数

四、日期加天数

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

五、日期减天数

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

七、运算符重载

八、日期减日期

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

9.1 流插入运算符 operator<< 重载

9.2 流提取运算符 operator>> 重载


一、const成员

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

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

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}private:int _year; // 年int _month; // 月int _day; // 日
};void Test()
{Date d1(2022,1,13);d1.Print();const Date d2(2022,1,13);d2.Print();
}

根据上述代码,我们来解决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=(&d2, 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;
}
a// ==运算符重载(const修饰,简化返回)
bool operator==(const Date& d) const {return _year == d._year && _month == d._month && _day == d._day;
}// >=运算符重载(const修饰)
bool operator >= (const Date& d) const {return *this > d || *this == d;
}// <运算符重载(const修饰)
bool operator < (const Date& d) const {return !(*this >= d);
}// <=运算符重载(const修饰)
bool operator <= (const Date& d) const {return !(*this > d);
}// !=运算符重载(const修饰)
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;// 区分大小日期,修正flagif (*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)

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

9.1 流插入运算符 operator<< 重载

ostream& operator<<(ostream& out, const Date& d)
{out<<d._year<<' '<< d._month <<' '<< d._day<<endl;return out;
}
  • 作用:实现自定义类型 Date 向输出流(如 cout)的打印
  • 细节:
    • 第一个参数 ostream& out 是输出流对象(如 cout),通过引用传递以支持链式调用(如 cout << d1 << d2;)。
    • 第二个参数 const Date& d 是要打印的 Date 对象,加 const 和引用是为了避免拷贝且保证对象不被修改。
    • 函数体中按 “年 月 日” 的格式输出 Date 的成员 _year_month_day,最后返回 out 以支持链式操作。

9.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 对象,同时进行日期合法性校验。
  • 细节:
    • 第一个参数 istream& in 是输入流对象(如 cin),引用传递支持链式调用(如 cin >> d1 >> d2;)。
    • 第二个参数 Date& d 是要赋值的 Date 对象,引用传递以直接修改其成员。
    • 先从输入流读取 yearmonthday 三个整数。
    • 通过 if 判断日期合法性:
      • 月份 month 需在 1-12 之间;
      • 天数 day 需大于 0,且不超过当月最大天数(由 d.GetMonthDay(year, month) 函数判断,该函数需实现 “判断某年某月有多少天” 的逻辑,比如二月闰年 29 天、平年 28 天等)。
    • 若合法,将读取的数值赋值给 d 的成员;若非法,输出 “非法日期” 并通过 assert(0) 断言终止程序(实际项目中可替换为更友好的错误处理)。
    • 最后返回 in 支持链式输入。

【注意】:

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

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

相关文章:

  • GNN应用:网站结构建模(二)
  • 无锡建设企业网站商品促销活动策划方案
  • 建设网站为网站网站做广告爱网站无法登录怎么回事
  • Ubuntu 24.04 更换国内软件源(以阿里云为例)
  • 【Advanced Engineering Informatics 1区TOP】ELA-YOLO:一种基于线性注意力的高效钢铁表面缺陷检测方法
  • 【优选算法】LinkedList-Concatenate:链表的算法之契
  • 网站建设哪家go好app开发公司倒闭了怎么办
  • 创世网站建设 优帮云制作网页的步骤
  • LIN总线校验和对比解析
  • Vue.js 响应接口
  • 上海外贸建站黟县网站建设
  • 前进方向坡度角算法开发计划
  • ps插件国外网站网站建设需要哪些步骤 谢谢
  • 织梦cms做好的网站怎样上传到服务器中国住房与城乡建设厅网站
  • 在线教程丨Deepseek-OCR以极少视觉token数在端到端模型中实现SOTA
  • Gorm(十四)的多条件叠加
  • 网站设计班培训郑州网站关键词排名技术代理
  • 网络流dinic与EK
  • 网络编程核心:套接字绑定(bind函数)与 IP 地址转换处理
  • 百度建站东莞著名网站建设
  • 如何选择邯郸网站制作做外贸网站维护费是多少
  • 【SCI复现】高比例可再生能源并网如何平衡灵活性与储能成本?虚拟电厂多时间尺度调度及衰减建模
  • CodeBuddy AI IDE:全栈AI开发平台实战
  • 购物网站开发教程 视频大流量网站 文章点击
  • 研究人员诱导ChatGPT对自身实施提示注入攻击
  • 数据结构与算法实验(黑龙江大学)
  • 孤客截图工具 Pro - 从开发到打包的完整指南
  • 山东德州最大的网站建设教学学校网站php源码|班级主页教师博客学生博客|学校网站织梦仿
  • 基于librespot的定制化Spotify客户端开发:开源替代方案的技术实践与优化
  • 主从同步配置的步骤