C++ 类和对象 日期类的实现

简介: C++ 类和对象 日期类的实现

本章目标


1.掌握日期类的实现

2.了解剩下两个默认函数


一. 日期类的实现


对于日期类来说 其成员变量包括年 月 日这三个


它的通常操作有


日期加天数 计算多少天后是什么时间 是周几


日期减天数 计算多少天前是什么时间 是周几


日期减日期 计算两个日期之间相差多少天 相差多少周


日期加日期没有什么意义 这里不做实现


我们都知道 每个月的天数都不尽相同 并且还有闰年这个影响因素 所以说我们首先要实现一个Getmonthday的函数

它的主要作用是得到某年某月的具体天数


1.1 Getmonthday的实现


思路分析: 首先每个月的天数都不同 我们可以创建一个数组 来填上所有月数对应的天数 类似于这样子


int monthDayArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };


当然 我们这里的月份肯定是1~12月 所以为了更严谨可以在前面加个断言


assert(month > 0 && month < 13);


当然还有一个影响月天数的原因就是是否是闰年


所以说还需要再写以一个判断是否是润年的函数


四年润 百年不润 四百年润


bool isleapyear(int year)
{
  if ((year%4==0 && year%100 !=0) || (year%400==0))
  {
  return true;
  }
  else
  {
  return false;
  }
}


然后我们整体代码表示如下


public:
  int Getmonthday(int year,int month)
  {
  assert(month > 0 && month < 13);
  int monthDayArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  // 判断是否是闰年 如果是闰年 二月的天数就是28 
  if (month==2 && isleapyear(year))
  {
    return 29;
  }
  // 否则就返回这个月的天数
  else
  {
    return monthDayArr[month];
  }
  }


想想看 还有没有什么值得优化的地方


这个获取月份的数组 我们是不是要经常使用啊


每次都要创建销毁数组未免也太浪费内存了 我们可以用static关键字修饰下这个数组 将其中的内容


存放到静态区来进行更好的资源管理


1.2 构造函数和打印函数


我们实现了得到月份功能后迫不及待想试验一下了


那么试验前我们首先要对对象进行初始化 然后打印其数据看看是否正确


这两步在前面的博客中已经做过详细讲解 这里不再赘述


构造函数代码如下


Date(int year = 1,int month =1,int day =1)
  {
  // 判断年月日输入是否正确 
  assert(year >= 1);
  assert(month >= 1 && month <= 12);
  assert(day >= 0 && day <= 31);
  // 赋值 这里用this指针也可以
  this->_year = year;
  _month = month;
  _day = day;
  }


打印函数代码如下


void Print()
  {
  cout << _year << "-" << _month << "-" << _day << endl;
  }


下面我们来测试下三种情况


1 闰年的二月份


877044b170b348fd95d044a9c88d8c38.png


2 非闰年的二月份


9494b17634054897bd16bc71fe0f8dd8.png


3 错误的日期

a27b68d594e64abb91777def5ac18f85.png


我们可以发现 这三个场景都符合我们的预期


1.3 日期类的运算函数


== 符号


这个很简单 依次判断三个值是否相等就可以


bool operator == (const Date& d)
  {
  return _year == d._year
    && _month == d._month
    && _day == d._day;
  }

> 符号


实现大于号的思路很简单


如果首先判断年 如果年大于就大于


如果年相同判断月 如果月大于就大于


最后判断日 如果日大于就大于


但是我们判断完年之后是否可以直接返回一个bool类型呢?


很显然不可以


这个时候我们换一个思路 如果小于等于就返回false


代码表示如下


bool operator > (const Date& d)
  {
  bool operator > (const Date& d)
  {
  if (_year>d._year)
  {
    return true;
  }
  if (_month>d._month && _year == d._year)
  {
    return true;
  }
  if (_day<d._day && _month == d._month && _day == d._day)
  {
    return true;
  }
  return false;
  }


<= 符号


这里很简单 不大于不就是小于等于嘛?


所以这里直接上代码


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


< 符号

不大于的同时不等于 就是小于


return (*this) <= d && !((*this) == d);


>= 符号


不小于就是大于等于


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


1.4 日期类的加减天数的实现


+=天数


加上天数之后赋值 这个时候我们的返回值要改变


这里有个难点就是我们我们增加的天数万一很多怎么办


万一跨越了月份呢?


万一跨越了年份呢?


这里我们先从最简单的加上一天看起


如果只加上一天 并且加上这一天之后不会超过这个月天数的大小(想想看 怎么知道这个月有多少天)


那么就直接返回就可以了


如果说大于这个月份的天数呢?

是不是就要往后面的月份进位了啊

如果月份大于十二了呢?

是不是就要往后面的年进位了啊


按照这个思路我们来写代码


Date& operator += (int day)
  {
  assert(day >= 0);
  // 第一步 日期先加上
  _day = _day + day;
  while (_day>Getmonthday(_year,_month))
  {
    _day -= Getmonthday(_year, _month);
    _month += 1;
    if (_month>12)
    {
    _month -= 12;
    _year += 1;
    }
  }
  }

我们来验证下我们的思路


809517b0d3fc46a1b7587bf462cff8ee.png


完全正确


+天数

这里跟+=天数的区别 就是一个改变自身的值 一个不改变自身的值


使用一个中间值就好了


代码表示如下


Date& operator + (int day)
  {
  // 拷贝构造
  Date ret =(*this);
  ret += day; // 复用
  return ret;
  }


-=天数


和+=天数的思路差不多 转换下几个符号就可以了


代码表示如下


Date& operator -= (int day)
  {
  assert(day >= 0);
  // 第一步 日期先加上
  _day = _day - day;
  while (_day <= 0)
  {
    _month -= 1;
    if (_month < 1)
    {
    _month = 12;
    _year -= 1;
    }
    _day += Getmonthday(_year, _month);
  }
  return *this;
  }


6bab8da4030e4654b03d0830d96d3218.png

-天数


这个思路也很相似 不过多赘述


Date& operator + (int day)
  {
  // 拷贝构造
  Date ret = (*this);
  ret -= day; // 复用
  return ret;
  }


1.5 自增自减


由于 前置++ 和 后置++的特殊性 我们无法判断哪个是前置 哪个是后置


所以说C++中引入了以一个这样子的标准


C++规定:将括号中带有int的规定为后置++,不带int的为前置++ 。(int后面可以加参数,也可以不加)


其实也就是前置效率高那么一点点 所以C++就改变后置的类型去了


前置++ 前置–


这个很简单 使用下+=1 -=1就可以了

Date& operator ++ ()
  {
  // 复用+=
  (*this) += 1;
  return *this;
  }
  Date& operator -- ()
  {
  // 复用-=
  (*this) -= 1;
  return *this;
  }


后置++ 后置–


这个也很类似


使用下临时变量 返回临时变量就可以


Date& operator ++ (int x)
  {
  Date ret = *this;
  ret += 1;
  return ret;
  }
  Date& operator ++ (int x)
  {
  Date ret = *this;
  ret += 1;
  return ret;
  }


1.6 日期减日期


这个实现思路很简单


我们只需要选出两个中的较大值 然后让其中的较小值不停++ (并且设置一个计数器)


等到它们相等的时候就好了


代码表示如下


int operator - (const Date& d)
  {
  if (*this == d)
  {
    return 0;
  }
  Date min = *this;
  Date max = d;
  int count = 0;
  // 有可能相差天数为负数
  int flag = 1;
  if (*this>d)
  {
    max = *this;
    min = d;
    flag = -1;
  }
  while (!(max==min))
  {
    ++min;
    count= count+1;
  }
  return count*flag;
  }

我们可以发现 可以运行


639977b1c95b41f087539988a7cb7d1e.png


那么这就是日期计算器的全部内容啦


工程源代码如下


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<assert.h>
using namespace std;
bool isleapyear(int year)
{
  if ((year%4==0 && year%100 !=0) || (year%400==0))
  {
  return true;
  }
  else
  {
  return false;
  }
}
class Date
{
public:
  Date(int year = 1,int month =1,int day =1)
  {
  // 判断年月日输入是否正确 
  assert(year >= 1);
  assert(month >= 1 && month <= 12);
  assert(day >= 0 && day <= 31);
  // 赋值 这里用this指针也可以
  this->_year = year;
  _month = month;
  _day = day;
  }
  void Print()
  {
  cout << _year << "-" << _month << "-" << _day << endl;
  }
  int Getmonthday(int year,int month)
  {
  assert(month > 0 && month < 13);
  static int monthDayArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  // 判断是否是闰年 如果是闰年 二月的天数就是28 
  if (month==2 && isleapyear(year))
  {
    return 29;
  }
  // 否则就返回这个月的天数
  else
  {
    return monthDayArr[month];
  }
  }
  bool operator == (const Date& d)
  {
  return _year == d._year
    && _month == d._month
    && _day == d._day;
  }
  bool operator > (const Date& d)
  {
  // 我们大于能直接判断嘛?
  // 显然不能 所以说我们这里如果小于就返回false
  /*if (_year > d._year)
  {
  }*/
  if (_year<d._year)
  {
    return false;
  }
  if (_month<d._month)
  {
    return false;
  }
  if (_day<d._day)
  {
    return false;
  }
  // 最后还有一种全部相等的情况 
  if (*this == d)
  {
    return false;
  }
  return true;
  }
  bool operator <= (const Date& d)
  {
  return !(*this > d);
  }
  bool operator < (const Date& d)
  {
  return (*this) <= d && !((*this) == d);
  }
  bool operator >= (const Date& d)
  {
  return !((*this) < d);
  }
  Date& operator += (int day)
  {
  assert(day >= 0);
  // 第一步 日期先加上
  _day = _day + day;
  while (_day>Getmonthday(_year,_month))
  {
    _day -= Getmonthday(_year, _month);
    _month += 1;
    if (_month>12)
    {
    _month -= 12;
    _year += 1;
    }
  }
  return *this;
  }
  Date& operator + (int day)
  {
  // 拷贝构造
  Date ret =(*this);
  ret += day; // 复用
  return ret;
  }
  Date& operator -= (int day)
  {
  assert(day >= 0);
  // 第一步 日期先加上
  _day = _day - day;
  while (_day <= 0)
  {
    _month -= 1;
    // 这里首先要判断month是否越界 
    if (_month < 1)
    {
    _month = 12;
    _year -= 1;
    }
    _day += Getmonthday(_year, _month);
  }
  return *this;
  }
  Date& operator - (int day)
  {
  // 拷贝构造
  Date ret = (*this);
  ret -= day; // 复用
  return ret;
  }
  Date& operator ++ ()
  {
  // 复用+=
  (*this) += 1;
  return *this;
  }
  Date& operator -- ()
  {
  // 复用-=
  (*this) -= 1;
  return *this;
  }
  Date& operator ++ (int x)
  {
  Date ret = *this;
  ret += 1;
  return ret;
  }
  Date& operator -- (int x)
  {
  Date ret = *this;
  ret -= 1;
  return ret;
  }
  int operator - (const Date& d)
  {
  if (*this == d)
  {
    return 0;
  }
  Date min = *this;
  Date max = d;
  int count = 0;
  // 有可能相差天数为负数
  int flag = 1;
  if (*this>d)
  {
    max = *this;
    min = d;
    flag = -1;
  }
  while (!(max==min))
  {
    ++min;
    count= count+1;
  }
  return count*flag;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1(2001,7,5);
  Date d2(2022,11,4);
  //d1.Print();
  int ret = d1.Getmonthday(2001,2);
  cout << ret << endl;
  //d1 -= 1000;
  //d1.Print();
  int ret = d1 - d2;
  cout << ret << endl;
  return 0;
}

大佬们想到什么有趣的功能也可以加上去


二. 普通对象 const对象取地址

class Date
{
public :
    Date* operator&()
    {
        return this;
    }
    const Date* operator&()const
    {
        return this;
    }
private :
    int _year ; // 年
    int _month ; // 月
    int _day ; // 日
};


这里稍微了解下就好 基本不会用到这两个操作符


总结


本文主要讲解日期类的实现以及两个简单的默认构造函数


由于作者水平有限 出现错误在所难难免 希望大佬们看到之后能及时指正

如果本文帮助到了你 别忘了一键三连啊


阿尼亚 哇酷哇酷!

相关文章
|
19天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
29 2
|
25天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
59 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
65 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
74 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
25 1
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
30 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
26 4
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
2月前
|
存储 编译器 C语言
【C++类和对象(上)】—— 我与C++的不解之缘(三)
【C++类和对象(上)】—— 我与C++的不解之缘(三)