【C++】Date类的实现

发布时间:2026/10/12 6:15:57
【C++】Date类的实现 目录Date类成员变量Date类默认成员函数Date类构造函数Date类成员函数Date类日期比较大小函数判断日期类变量大于判断日期类变量等于判断日期类变量不等于判断日期类变量大于等于判断日期类变量小于判断日期类变量小于等于Date类加减天数函数获取月份天数函数日期加等天数日期加天数日期类变量前置日期类变量后置日期减等天数日期减天数日期类变量前置--日期类变量后置--日期减日期Date类日期流插入函数和流提取函数日期流插入函数日期流提取函数Date类成员变量Date 类的成员变量为私有访问属性的三个整型变量分别代表年、月、日即_year、_month、_day全部属于内置类型。class Date { public: //类函数声明/定义部分 //成员变量一般需要保护起来 private: int _year; int _month; int _day; };Date类默认成员函数对于日期类而言它的三个成员变量均不涉及动态资源的申请因此可以不用为其写析构函数而且它的三个成员变量也都不涉及指针因此拷贝构造函数和赋值运算符重载函数也不需要我们自己写因为编译器默认生成的浅拷贝对它而言就是够用的了。最后两个取地址重载函数对于绝大多数类而言一般都不需要我们自己实现。Date类构造函数在类的 public 部分声明构造函数 Date (int year 1, int month 1, int day 1);构造函数名字和类名 Date 保持一致构造函数没有返回值。这里给三个形参全部设置缺省值年、月、日默认都是 1。缺省参数规定写在类内的声明位置类外定义的时候不能重复书写缺省值重复写会编译报错。有了全缺省构造我们既可以不传参数写 Date d1;使用默认的 1‑1‑1 创建对象也可以传入实参 Date d2 (2025,10,1);使用自定义的年月日创建对象。类的私有部分定义三个整型成员变量_year、_month、_day用来存储对象的年、月、日外部不能直接访问这三个成员。Date::Date (int year, int month, int day) 是构造函数在类外面的定义Date:: 是类域限定符告诉编译器这个函数属于 Date 类。函数接收传入的 year、month、day首先做日期合法性判断判断条件分为三部分月份要大于 0 并且小于 13日期要大于 0同时日期不能大于当前年月对应的当月总天数这里调用 GetMonthDay 函数获取当月天数该函数需要提前实现要处理闰年二月的特殊情况。如果条件成立代表传入的是合法日期就把形参 year、month、day 赋值给对象的私有成员_year、_month、_day完成对象成员的初始化如果条件不成立属于非法日期就打印 “非法日期” 提示执行 assert (false)断言在调试模式会直接终止程序防止生成一个年月日无效的 Date 对象。这里要注意一个细节代码是先校验参数校验通过之后才给成员变量赋值。假如传入非法年月日_year、_month、_day 不会被赋值由于它们是内置 int 类型对象内存里会保留随机垃圾值。这是这段代码的一处小缺陷对象被创建出来但是成员是随机值实际工程中一般会先赋值再校验或者校验失败要给成员赋予安全默认值。class Date { public: //Date类构造函数声明(一般函数缺省值是在声明给,定义时不能再提供缺省值) Date(int year 1, int month 1, int day 1); private: int _year; int _month; int _day; }; Date::Date(int year, int month, int day) { //防止构造非法日期 if (month 0 month 13 day0 day GetMonthDay(year, month)) { _year year; _month month; _day day; } else { cout 非法日期 endl; assert(false); } }Date类成员函数Date类日期比较大小函数判断日期类变量大于函数后面的 const 修饰代表这个成员函数不会修改当前对象的成员变量const 类型的 Date 对象也可以调用这个函数参数使用 const Date x以引用方式接收对象避免传值带来的拷贝开销同时保护传入对象不会被修改。函数的比较逻辑按照先比年份、再比月份、最后比天数的顺序执行。首先判断当前对象的_year 是否大于 x 的_year如果年份更大直接返回 true代表当前日期更大。如果年份相等就继续判断月份当前对象_month 大于 x._month 的话返回 true。如果年份和月份全部相等就比较天数当前对象的_day 大于 x._day返回 true。上面三个条件只要有一个成立就说明当前日期大于 x。如果三层条件都不满足说明当前日期小于或者等于 x函数最后返回 false。举个例子比如当前对象是 2025‑10‑1传入 x 是 2025‑9‑30年份相同当前月份更大函数返回 true。如果当前对象是 2024‑5‑1x 是 2025‑3‑1当前年份更小前面条件全部不命中走到最后返回 false。写完 operator 之后其他比较运算符就可以复用这个函数不用重复写全套年月日判断逻辑。bool Date::operator(const Date x) const { if (_year x._year) { return true; } else if (_year x._year _month x._month) { return true; } else if (_year x._year _month x._month _day x._day) { return true; } return false; }判断日期类变量等于函数参数是 const Date x使用引用传参可以避免对象拷贝const 保证传入的对象在函数内部不会被修改。函数末尾的 const 修饰隐含的 this 指针表示该函数不会修改当前对象的成员const 修饰的 Date 对象也可以正常调用这个比较函数。函数使用 return 直接返回表达式结果判断逻辑要求年、月、日三者必须同时全部相等。 是逻辑与运算符只有_year 等于 x._year并且_month 等于 x._month并且_day 等于 x._day整个表达式结果才为 true代表两个日期完全相同。如果月份不一样即便年和日相同表达式也会返回 false。这个写好的相等判断函数还可以给不等于运算符复用直接对 operator 的返回结果取反即可不用再重新写一套年月日判断。bool Date::operator(const Date x)const { return _year x._year _month x._month _day x._day; }判断日期类变量不等于函数的参数是 const Date x引用传参避免发生对象拷贝const 保障传入的对象不会被修改。函数尾部带上 const代表函数不会修改当前对象的成员const 修饰的日期对象也能够调用这个函数。写法复用已经完成的相等判断逻辑减少重复代码也能保证判断标准统一不会出现相等和不等两套判断逻辑不一致的 bug。bool Date::operator!(const Date x)const { return !(operator(x)); }判断日期类变量大于等于参数使用 const Date x引用传参避免对象拷贝同时保护传入对象不被修改函数末尾的 const 表示不会修改当前对象const 修饰的日期对象也可以调用该函数。函数内部直接复用已经实现好的大于运算符 operator 和等于运算符 operator。*this 代表调用这个函数的当前对象|| 是逻辑或运算符只要两个条件里面有一个成立整体结果就为 true。如果当前日期大于 x或者当前日期等于 x就返回 true代表满足大于等于既不大于也不等于的时候就返回 false。这样复用已有接口可以减少重复代码保证各个比较运算符的判断规则保持一致。bool Date::operator(const Date x)const { return ( * this x || *this x ); }判断日期类变量小于参数使用 const Date x引用传参避免对象拷贝同时保护传入的对象不会被修改函数末尾的 const 说明该函数不会修改当前对象的成员const 修饰的日期对象也可以调用这个函数。函数内部直接复用已经写好的大于等于运算符 operator。this 代表调用函数的当前对象对 thisx 的运算结果取反。如果当前对象大于等于 x取反之后得到 false说明当前日期并不小于 x。如果当前对象不满足大于等于 x取反得到 true就代表当前日期小于 x。这种复用已有函数的写法可以减少重复代码也可以让所有比较运算符的判断标准保持统一避免出现逻辑不一致的问题。bool Date::operator(const Date x)const { return !(*thisx); }判断日期类变量小于等于参数使用 const Date x引用传参避免对象拷贝同时保护传入对象不会被修改函数末尾的 const 表示不会修改当前对象被 const 修饰的日期对象也可以调用该函数。直接复用已经实现好的大于运算符 operator。this 代表调用这个函数的当前对象! 是逻辑非运算符对this x 的结果进行取反。如果当前日期大于 x*thisx 结果为 true取反之后返回 false说明不满足小于等于。如果当前日期不大于 x也就是小于或者等于 x*thisx 结果为 false取反之后返回 true。复用已经写好的运算符能够减少重复代码保证全部比较运算符的判断标准统一。bool Date::operator(const Date x)const { return !(*thisx); }Date类加减天数函数获取月份天数函数函数内部定义了 static 修饰的整型数组 monthDays数组下标从 0 到 12下标 0 位置的数据没有实际用处下标 1 到 12 依次对应十二个月的天数平年二月默认存 28。数组前面加 static数组就存放在静态数据区函数多次调用的时候不会重复创建数组程序运行期间数组只初始化一次以此提升执行效率。如果当前是 2 月份并且满足闰年的判定条件普通闰年能被 4 整除但是不能被 100 整除世纪闰年可以被 400 整除满足条件就返回 29 天。不是闰年的二月或者是其他月份直接从 monthDays 数组取出对应下标的天数返回。把二月的判断放在前面大部分月份不会进入这个 if 分支可以减少判断开销。要注意这个函数没有做月份合法性校验调用这个函数的时候传入的 month 必须是 1 到 12 之间的数值否则数组下标越界会出现问题。闰年的判断只针对二月其余月份的天数直接读取数组不受闰年影响。int Date::GetMonthDay(int year, int month) { static int monthDays[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; } else { return monthDays[month]; } }日期加等天数给当前日期对象增加指定天数会直接修改调用函数本身的对象返回 Date 引用类型目的是支持连续的复合赋值操作。函数首先判断传入的 day 是否小于 0如果 day 是负数就复用已经写好的减等于运算符把 - day 传入相当于执行日期减去正数直接 return *this - -day处理传入负数天数的场景。如果 day 是正数先把 day 直接加到当前对象的_day 上。之后进入 while 循环循环的条件是当前的_day 大于当月的总天数通过 GetMonthDay 获取当月的天数。当日期超过本月最大天数就用_day 减去本月的天数月份加一。月份增加之后再判断如果月份大于 12说明需要跨年年份加一月份减去 12 变回 1。循环会一直重复这个进位逻辑直到_day 不再大于当月天数日期就调整为合法的新日期。最后返回 * this也就是当前对象的引用支持像 d1 10 20 这样连续调用。这个函数会直接修改原对象和 operator 不一样operator 会生成临时对象不会改动原来的日期。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 12) { _year; _month - 12; } } return *this; }日期加天数日期类普通加号运算符的重载函数作用是实现日期加上天数这个函数不会修改调用函数的原日期对象所以函数尾部加了 const 修饰。返回值类型是 Date不是引用因为函数里面创建了局部临时对象 tmp局部对象出函数作用域就会销毁不能用引用返回。函数内部先用 Date tmp (this)调用拷贝构造把当前对象this 拷贝一份给临时对象 tmptmp 就是原日期的副本。接着调用 tmp day借助已经实现好的 运算符给临时对象增加天数这一步只会修改 tmp原来的对象不会发生任何变化。最后把修改完成的临时对象 tmp 返回出去。使用的时候比如 Date d2 d1 100d1 本身保持不变计算出来的新日期存到 d2 里面。这里要区分开 operator 和 operator 是直接修改自身 是生成一份新对象做运算原对象保持不变。因为返回的是局部临时对象不能返回引用只能以值的方式返回。Date Date::operator(int day)const { Date tmp(*this); tmp day; return tmp; }日期类变量前置Date 类前置 运算符的重载函数对应 d 这种写法作用是把当前日期向后增加一天。返回值是 Date 引用类型可以返回当前对象本身。函数里面直接复用已经写好的 运算符执行this 1也就是给当前对象的日期加上一天这一步会直接修改调用该函数的对象。运算完成之后返回this也就是返回自身对象的引用。前置 不需要创建临时对象直接修改原对象并且返回自身引用所以效率相对更高因此自定义类型一般优先推荐使用前置 。返回引用也支持连续运算比如 d 这样的写法。注意这个函数没有写 const因为它会修改对象的成员变量const 修饰的日期对象不能调用这个函数。Date Date::operator() { *this 1; return *this; }日期类变量后置Date 类后置 运算符的重载函数对应 d 这种写法功能是日期向后增加一天但是返回自增操作之前的旧值。函数参数里面写了 int这里只是一个占位参数没有写参数名字这个参数不会在函数内部被使用C 靠这个额外的 int 形参和前置 构成函数重载用来区分前置 和后置 。函数首先用 Date tmp (this) 拷贝构造一个临时对象 tmp把当前对象还没有自增之前的日期保存下来。接着执行this 1调用 运算符把调用函数的原对象日期加一天原对象本身会被修改。最后返回临时对象 tmptmp 里面存放的是自增之前的旧日期。返回类型是 Date不是引用因为 tmp 是函数内部的局部临时对象函数结束之后 tmp 就会销毁不能返回局部对象的引用只能做值返回。后置 需要创建临时对象保存旧数据相比前置 会多一份拷贝所以自定义类型优先使用前置 。这个函数会修改对象成员所以末尾不能加 const。Date Date::operator(int) { Date tmp(*this); *this 1; return tmp; }日期减等天数日期类减等于运算符的重载函数用来实现当前日期减去指定天数函数会直接修改调用该函数的对象返回 Date 引用是为了支持连续复合赋值。首先判断传入的 day 是否小于 0如果 day 是负数就复用已经实现的 运算符把 - day 当做正数相当于日期加上正数天数直接返回 * this -day处理传入负天数的情况。如果 day 是正数先把 day 直接从当前对象的_day 上面减掉。进入 while 循环循环条件是_day 小于等于 0代表日期不够需要向上一个月借天数。首先把月份减一如果月份减完之后小于等于 0就代表需要跨年年份减一月份加上 12 回到 12 月。处理完年月之后调用 GetMonthDay 拿到刚刚跳转过去的上一个月的总天数加到_day 上面。循环会不断执行借位逻辑直到_day 大于 0得到合法的日期。处理完成之后返回 * this也就是当前对象的引用支持连续调用函数会直接改动原对象和 operator - 区分开operator‑会生成临时对象不会修改原本的日期。函数内部会修改成员变量所以不能加 const 修饰。Date Date::operator-(int day) { if (day 0) { return *this -day; } _day - day; while (_day 0) { --_month; if (_month 0) { _year--; _month 12; } _day GetMonthDay(_year, _month); } return *this; }日期减天数日期类减号运算符重载函数实现日期减去天数的功能函数末尾的 const 代表不会修改调用这个函数的原对象。返回类型是 Date不能返回引用因为 tmp 是函数内部创建的局部对象函数执行结束后局部对象会销毁返回引用会产生野引用。函数内部先用 Date tmp (*this) 调用拷贝构造函数复制一份当前对象保存到临时对象 tmp 中。接着调用已经写好的减等于运算符对 tmp 减去 day 天修改的只有 tmp原来的日期对象不会发生变化。最后把 tmp 以值拷贝的方式返回。使用的时候写 Date d2 d1 - 20d1 本身不会改变计算得到的新日期存放在 d2。和 operator- 对比operator- 直接修改原对象而 operator - 会拷贝一份对象做运算保留原始数据不变。Date Date::operator-(int day)const { Date tmp(*this); tmp - day; return tmp; }日期类变量前置--Date 类前置--运算符的重载函数对应 --d 这种写法作用是把当前日期向前减少一天。返回值是 Date 引用返回对象自身。函数内部复用已经实现好的减等于运算符执行this - 1直接修改调用函数的原对象让日期减去一天。运算完成之后返回this也就是当前对象的引用。前置 -- 不需要创建临时对象直接修改原对象效率更高也支持连续运算比如 ----d。因为函数会修改对象的成员变量所以不能在函数末尾添加 const 修饰。Date Date::operator--() { *this - 1; return *this; }日期类变量后置--Date 类后置--运算符的重载函数对应 d-- 这种写法作用是将日期向前减少一天但是返回减减操作之前的旧日期。函数参数里的 int 是占位参数不需要写参数名仅仅用来和前置 -- 构成函数重载区分前置和后置版本。函数内部首先用 Date tmp (this) 拷贝构造临时对象 tmp保存当前对象还没有自减之前的日期。接着执行this - 1调用已经实现好的减等于运算符修改原对象让原日期减去一天。最后返回临时对象 tmptmp 里面存放的是自减之前的旧日期。返回类型是 Date不能返回引用因为 tmp 是函数内部的局部对象函数结束之后 tmp 就会销毁返回引用会出现野引用问题。后置 -- 需要创建临时对象保存旧值相比前置 -- 会多一次对象拷贝运行效率更低。函数会修改对象的成员所以不能在函数末尾加 const。Date Date::operator--(int) { Date tmp(*this); *this - 1; return tmp; }日期减日期日期类相减运算符重载函数用来计算两个日期之间相差的天数也就是当前日期减去传入的日期对象 x函数末尾带 const代表不会修改当前对象。函数返回 int 类型结果可以是正数、负数或者 0正数代表当前日期比 x 晚负数代表当前日期比 x 早。函数首先定义变量 count 用来记录相差的天数再拷贝一份 x 保存到临时对象 tmp。接着判断当前日期 * this 是否小于 tmp如果当前日期更早就进入第一个循环。只要两个日期不相等count 就减一tmp 执行后置减减也就是 tmp 向前走一天直到 tmp 和当前日期相等最后返回 count得到负数的天数差。如果当前日期大于或者等于 x就进入第二个循环只要两个日期不相等count 加一tmp 执行后置加加tmp 向后增加一天直到两个日期相同返回 count得到正数的天数差。这个实现依靠循环一天一天推移日期来统计天数逻辑简单易懂但是时间效率不高两个日期相隔很远的时候循环次数会很多。函数不会修改原对象和传入的 x 对象只操作临时拷贝的 tmp。//日期减日期 int Date::operator-(const Date x)const { int count 0; Date tmp(x); if (*this tmp) { while (*this ! tmp) { count--; tmp--; } return count; } else { while (*this ! tmp) { count; tmp; } return count; } }

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询