C++类和对象 练习小项目---日期类的实现.

简介: C++类和对象 练习小项目---日期类的实现.

前言

这是我们需要实现的日期类的接口声明,我们需要的是在Date.cpp文件中实现函数的定义.

class Date
{
public:
  // 获取某年某月的天数
  int GetMonthDay(int year, int month);
  //打印日期类函数
  void Print();
  // 全缺省的构造函数
  Date(int year = 1900, int month = 1, int day = 1);
  // 拷贝构造函数
  Date(const Date& d);
  // 赋值运算符重载
  Date& operator=(const Date& d);
  // 析构函数
  ~Date();
  // 日期+=天数
  Date& operator+=(int day);
  // 日期+天数
  Date operator+(int day);
  // 日期-天数
  Date operator-(int day);
  // 日期-=天数
  Date& operator-=(int day);
  // 前置++
  Date& operator++();
  // 后置++
  Date operator++(int);
  // 后置--
  Date operator--(int);
  // 前置--
  Date& operator--();
  // >运算符重载
  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);
  // 日期-日期 返回天数
  int operator-(const Date& d);
private:
  int _year;
  int _month;
  int _day;
};

一、构造函数

1.1 默认构造函数

声明:(在Date类中)

//Date.h
  // 全缺省的构造函数
  Date(int year = 2023, int month = 1, int day = 1);

定义:

//Date.cpp
Date::Date(int year , int month, int day)
{
  _year = year;
  _month = month;
  _day = day;
  //这里也就体现出了,成员变量前面'_'的好处,方便与参数区分
}

这里需要注意的是,缺省参数应该在声明处给出,定义时不能有缺省参数,在C++入门章节牛牛有提到过原理.

1.2 拷贝构造函数

使用场景:

Date d1(2023, 4, 26);
Date d2(d1);//使用已存在的对象去初始化另一个对象,被称为拷贝构造

义:

//Date.cpp
// 拷贝构造函数
Date::Date(const Date& d)
{
  _year = d._year;
  _month = d._month;
  _day = d._day;
}

注意使用引用传参.

二、获取天数

放在以前,牛牛实现获取天数的函数可能会用一个很长的Switch case语句,然后返回每一个天数的时间.


如今,牛牛发现,除了闰年时2月是29天以外,其他时候,每个月的时间是不变的,我们可以使用数组将每个月的天数存起来.

int Date::GetMonthDay(int year, int month)
{
  int day[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))//如果是闰年,且是2月
  {
    day[2] = 29;
  }
  return day[month];
}

这里需要注意的是判断条件month == 2要放在前面

((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)放在后面,因为前面的判断条件很好判断,后面的比较复杂,这样可以提高效率.

三、运算符重载

3.1 赋值运算符重载

注意:

参数const Date& d


1.const一方面保证右操作数不会被修改,即防止函数写反了功能.

例如:参照错误写法.

d1=d2,结果是将d2修改成了d1,偷鸡不成蚀把米.


2.Date&传引用就不会调用拷贝构造函数去传参,减少拷贝,提高效率.

//运算符重载
Date& Date::operator=(const Date& d)
{
  _year = d._year;
  _month = d._month;
  _day = d._day;
  return *this;
}
//错误写法
Date& Date::operator=(Date& d)
{
  d._year=_year; 
  d._month=_month;
  d._day=_day;
  return *this;
}

3.2 日期+=天数

ef7068a5ae914ce980b69711e73fff3a.png

示例: 2023年7月28日+80天

f9919a78f5ff4785b884a5dd007b6619.png

需要注意的是,如果month往后推一位后为13,则应当将month设置为1,并且year++.


代码实现:

// 日期+=天数
Date& Date:: operator+=(int day)
{
  if (day < 0)//不知道使用者会不会调皮传过来负数
  {
    return *this -= -day;//调用"-="的运算符重载
  }
  _day += day;
  while (_day > GetMonthDay(_year, _month))//如果超过当月天数
  {
    _day -= GetMonthDay(_year, _month);//通过调用GetMonthDay函数获取当月天数
    _month++;
    if (_month > 12)//月数超过12,则开始下一年
    {
      _month = 1;
      _year++;
    }
  }
  return *this;
}

3.3 日期+天数

与日期+=天数不同,日期+天数要求该日期本身没有改变,而是返回日期+天数后的日期


此时,我们需要创建一个临时Date 类ret,将增加的天数与ret进行计算,最后返回ret对象.

// 日期+天数
Date Date:: operator+(int day)
{
  if (day < 0)
  {
    return *this -= -day;//调用"-="的运算符重载
  }
  Date ret;//创建临时对象
  ret._day += day;
  while (ret._day > GetMonthDay(_year, _month))//如果超过当月天数
  {
    ret._day -= GetMonthDay(_year, _month);
    ret._month++;
    if (ret._month > 12)
    {
      ret._month = 1;
      ret._year++;
    }
  }
  return ret;
}

3.4 日期-=天数 和 日期-天数

f3ec1d9246aa47f1b5a7ae41a5f06d1c.png

示例:2023年7月28日-100天


需要注意的是,重点是+上月的天数,而不是本月的天数.

ddffc50839ee46fe97a259e742e765ce.png

 // 日期-=天数
Date& Date:: operator-=(int day)
 {
   if (day < 0)
   {
    return *this += -day;//调用"+="的运算符重载
   }
   _day -= day;
   while (_day <= 0)//如果是负数
   {
     _month--;
     if (_month <= 0)
     {
       _month = 12;
       _year--;
     }
     _day += GetMonthDay(_year, _month);
   }
   return *this;
 }

到了这里,我想日期-天数的实现应该比较简单,牛牛就不多介绍了.

// 日期-天数
 Date Date::operator-(int day)
 {
   if (day < 0)
   {
     *this -= day;//调用"-="的运算符重载
   }
   Date ret;
   ret._day -= day;
   while (ret._day <= 0)//如果是负数
   {
     ret._day += GetMonthDay(_year, _month-1);//+上个月的总天数
     ret._month--;
     if (ret._month <=0)
     {
       ret._month = 12;
       ret._year--;
     }
   }
   return ret;
 }

3.5 日期-日期

fc358814e15342a9beeccae4bbdaea5b.png

日期-日期怎么计算?

例如:

2023年7月28号距离2024年1月1号还有几天?

如果对应的年月日进行想减,然后还需要计算是那些年有那些天,月数又有几天,那可就太麻烦了吧.


所以我们直接先判断两个日期的大小,选择用较小的日期,对齐进行++操作,直到与较大的相等,统计++了多少天,这样是不是就很简单了?


步骤:


   1.比较日期大小,选出较小者.

   2.对较小者进行++并统计,直到与较大者相等.

   3.返回统计的天数.

// 日期-日期 返回天数
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)//用n统计相差多少天
  {
    ++min;
    ++n;
  }
  return n * flag;
}

3.6 前置++与后置++

前置++与后置++实现的时候有一个很尴尬的问题,因为前置++和后置++都是单目运算符,即只有一个操作数,那么为了实现他们两个函数能够重载,则只能在后置++处添加一个int类型的参数.

这个参数用户在使用时不需要传递,编译器会自动传递,本质是为了让前置++和后置++进行函数重载.


前置++是返回+1之后的结果,并且this是指向对象本身的,所以我们可以使用传引用返回,减少拷贝,提高效率.


后置++是返回+1之前的值,并且对象最终还需要被修改,所以我们需要创建一个临时对象用于记录+1前对象的日期大小.除此之外,因为临时变量是在局部定义的,所以我们必须传值返回,不能传引用返回.

// 前置++
 Date& Date:: operator++()
{
   _day +=1;
   while (_day > GetMonthDay(_year, _month))//如果超过当月天数
   {
     _day -= GetMonthDay(_year, _month);//则减去当月的天数
     //月份向后推一个月
     _month++;
     if (_month > 12)
     {
       _month = 1;
       _year++;
     }
   }
   return *this;
}
// 后置++
Date Date::operator++(int)//这个参数为了个与前置++构成函数重载,调用的时候不需要传参.
{
  Date tmp = *this;//要保存++前日期的大小.
  _day += 1;
  while (_day > GetMonthDay(_year, _month))//如果超过当月天数
  {
    _day -= GetMonthDay(_year, _month);
    _month++;
    if (_month > 12)
    {
      _month = 1;
      _year++;
    }
  }
  return tmp;
}

3.7 前置–与后置–

学了前置++与后置++,这里也是类似的,需要注意的是,+上月的天数.

// 前置--
Date& Date::operator--()
{
  _day -= 1;
  while (_day <= 0)//如果是负数
  {
    _day += GetMonthDay(_year, _month - 1);//+上个月的总天数
    _month--;
    if (_month <= 0)
    {
      _month = 12;
      _year--;
    }
  }
  return *this;
}
// 后置--
Date Date::operator--(int)
{
  Date tmp = *this;
  _day -= 1;
  while (_day <= 0)//如果是负数
  {
    _day += GetMonthDay(_year, _month - 1);//+上个月的总天数
    _month--;
    if (_month <= 0)
    {
      _month = 12;
      _year--;
    }
  }
  return tmp;
}

3.8 比较运算符

比较运算符重载我不多介绍了,没有什么难度.


需要学习的是,可以使用已经实现的>和"=="去复用实现剩下的其他运算符

bool Date::operator>(const Date& d)
{
  if (_year > d._year)//如果年大
  {
    return true;
  }
  {
    if (_year == d._year)
    {
      if (_month > d._month)//年相同,月大
      {
        return true;
      }
      else
      {
        if (_day > d._day)//入如果年月相同,日大
        {
          return true;
        }
        return false;//月小,或者日小和相等
      }
    }
    return false;//如果年小
  }
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
  if (_year == d._year&&
    _month== d._month&&
    _day==d._day
    )
  {
    return true;
  }
  return false;
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
  if (*this > d|| *this == d)
  {
    return true;
  }
  return false;
}
// <运算符重载
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天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
34 4
|
8天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
31 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
19 1