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);
}

日期类就简单实现到这里了,友友们下次见,总代码,可以去我的资源处下载源代码哦.

目录
相关文章
|
11天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
51 18
|
11天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
37 13
|
11天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
37 5
|
11天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
26 5
|
11天前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
32 4
|
11天前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
26 3
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
76 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
128 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
138 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
195 4