【C++初阶】类和对象修炼下

简介: 【C++初阶】类和对象修炼下

类和对象下呐主要是给大家讲一下日期类的实现,至于为什么不实现一个栈呐,那是因为目前学到的拷贝构造和赋值重载都是属于一种浅拷贝,而对于栈类我们需要使用深拷贝.

一.四大默认成员函数

这里就不过多赘述了

class Date
{
public:
  //全缺省的构造函数
  Date(int year = 2022, int month = 10, int day = 13)
  {
    _year = year;
    _month = month;
    _day = day;
    if (!(_year > 0
      && _month >= 1 && _month <= 12
      && _day >= 1 && _day <= GetMonthDay(_year, _month)))
    {
      cout << "输入的日期不合法" << endl;
      Print();
    }
  }
  //拷贝构造(其实对于日期类可以不用写拷贝构造)
  Date(Date& d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
  //赋值重载(其实对于日期类可以不用写拷贝构造)
  Date& operator=(const Date& d2)
  {
        _year = d2._year;
      _month = d2._month;
        _day = d2._day;
     return *this;
  }
  //析构函数:(其实对于日期类可以不用写析构函数)
  ~Date()
  {
    _year = _month = _day = 0;
  }
  //打印日期
  void Print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  // 内置类型
  int _year;
  int _month;
  int _day;
};

注意:下面所写的函数都是日期类中的成员函数,这就意味着第一个参数都是隐含的this指针.

二.获取某年某月的天数

年分为闰年和平年,月也分为1-12月,所以对于任意一年的12个月中每一个月的天数都是基本一样的,维度在2月因为平年还是闰年相差一天.所以如果你要获取某年某月的天数,就只需对于在2月,且是闰年特殊+1天就可以.

  //获取某年某月的天数
  int GetMonthDay(int year, int month)
  {
    int monthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    int day = monthDay[month];
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0 && month == 2)
    {
      ++day;
    }
    return day;
  }

这里值得一提的是,这里有两个可以优化的地方:


这里的GetMonthDay()函数会被反复调用,且monthDay数组始终不变,所以建议定义成static静态的

这里的if判断条件中,除法和取余的运算符效率低,所以建议把month==2的条件写在最前面

也就是这样:

  //获取某年某月的天数
  int GetMonthDay(int year, int month)
  {
    int _month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    int _day = _month[month];
    if (month == 2 && year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
    {
      ++_day;
    }
    return _day;
  }

三.日期+=天数和日期+天数

这里可以通过举例内置类型中+=和+运算符的使用特点

比如: c=a+=b和c=a+b这个例子

+=和+有一定的相似点:

返回值和参数在类型和个数上都是一样的


返回值:Date&


参数1:隐含的this指针


参数2:要+的天数


但是,+=和+也有不同点:


c=a+=b这里a的值在运算前后发生改变,


c=a+b这里a的值在运算前后没有改变.


这里日期1+=天数和日期1+天数两个函数的区别在于:


日期1+=天数中,日期1在运算前后发生改变,

日期1+天数中,日期1在运算前后没有改变.

这里先给大家实现operator+=(int day)函数:


a3fc557ec2e17583f4ee42afa85666bb.png

ps:值得注意的是12到1月的转变的时候,年份也要变

  Date& operator+=(int day)
  {
    if (day < 0)
    {
      return *this -= -day;
    }
    _day += day;
    while (_day > GetMonthDay(_year, _month))
    {
      _day -= GetMonthDay(_year, _month);
      ++_month;
      if (_month == 13)
      {
        _month = 1;
        ++_year;
      }
    }
    return *this;
  }

6a8c671bdc44ec81aaceb4a6ba47ce51.png

然后我们来实现一下,operator+(int day)函数:

这里我们可以调用拷贝构造一个替身,让替身的this发生改变,然后返回替身,那么就可以得到返回值的同时,本体的this却没有发生改变.

  //日期+天数
  Date operator+(int day)
  {
    Date ret(*this);
    if (day < 0)
    {
      ret -= -day;
      return ret;
    }
    ret += day;
    return ret;
  }

四.日期-=天数和日期-天数

这里和三的区别和相同点一样,这里不过多赘述.

这里是日期-天数,那么同样的先全部减在天数上,然后借位,值得注意的是12到1月的转变的时候,年份也要变

1e8c487a03462513ff5e94324ae97589.png

日期-=天数:

  //日期-=天数
  Date& operator-=(int day)
  {
    if (day < 0)
    {
      *this += -day;
    }
    _day -= day;
    while (_day <= 0)
    {
      --_month;
      if (_month == 0)
      {
        _month = 12;
        --_year;
      }
      _day += GetMonthDay(_year, _month);
    }
    return *this;
  }

日期-天数:

  //日期-天数
  Date operator-(int day)
  {
    Date ret(*this);
    if (day < 0)
    {
      ret += -day;
      return ret;
    }
    ret -= day;
    return ret;
  }

ps:这里传入的天数一般都是正数,如果天数是负数的话,日原来的日期-天数就想当与日期+(-天数),同样可以代码复用.

五.日期比较

这里代码书写起来比较简单,这里只想给大家讲一下复用的问题:写了>和==其他日期比较,比如>=或者<或者!=都可以通过函数复用实现.

  //==运算符
  bool operator==(const Date& d)
  {
    return _year == d._year
      && _month == d._month
      && _day == d._day;
  }
  //<运算符重载
  bool operator<(const Date& d)
  {
    return !(*this > d || *this == d);
  }

复用>和==的代码,可以完成:

  //>=运算符
  bool operator>=(const Date& d)
  {
    return *this > d || *this == d;
  }
  //<=运算符
  bool operator<=(const Date& d)
  {
    return !(*this > d);
  }
  //!=运算符
  bool operator!=(const Date& d)
  {
    return !(*this == d);
  }

六.日期++和++日期

首先,日期++和++日期也就是我们常说的b=a++和b=++a的区别:

但是这里会出现一个比较尴尬的问题:

这里的要写的两个运算符重载函数都使用的是一个运算符++,所以在书写成员函数的时候函数名肯定都是


operator++,那么当我们写同时使用了d2=d1++和d2=++d1的时候,我们就写一个函数肯定不行,所以C++语法就规定:


前置++和后置++的运算符重载函数使用operator++作为函数名,但是前置++的运算符重载函数不带参数,后置++的运算符重载函数带一个int类型作以区分.

  //前置++: 比如y=++x;
  Date& operator++()
  {
    *this += 1;
    return *this;
  }
  //后置++: 比如y=x++;
  Date operator++(int)
  {
      //记录最初值
    Date ret(*this);
    *this += 1;
        //返回最初值
    return ret;
  }

值得注意的是,后置++中只能传值返回,因为出了作用域ret就不存在了,不能传引用返回.

所以后置++这里要调用两次拷贝构造,一般推荐使用前置++

七.日期-日期

日期+日期就和指针+指针一样,没有任何意义,所以这里不讨论日期+日期

另外运算的结果是两个日期相差的天数,所以没法日期-=日期

所以我们只讨论日期-日期:

方法1:从日期1遍历到日期2计数,直至相等,推荐

  //日期-日期: 
  int operator-(const Date& d)
  {
    Date max = *this;
    Date min = d;
    int flag = 1;
    if (*this < d)
    {
      max = d;
      min = *this;
      flag = -1;
    }
    int count = 0;
    while (min != max)
    {
      ++min;
      ++count;
    }
    return flag * count;
  }

方法2:选定一个起始位置0-1-1,分别计算日期1和日期2到起始位置的天数,然后两个天数相减

注意:这里要先求从0到_year-1这几年的整年天数,再算当前年从1- _month-1这几月的天数,最后再加上 _day

  //日期-日期: 
  int GetDay(const Date& d)
  {
    int day = 0;
    for (int i = 0; i < d._year; i++)
    {
      day += 365;
      if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0)
      {
        ++day;
      }
    }
    for (int i = 1; i < d._month; i++)
    {
      day += GetMonthDay(d._year, i);
    }
    day += d._year;
    return day;
  }
  int operator-(const Date& d)
  {
    int day1 = GetDay(*this);
    int day2 = GetDay(d);
    return day1 - day2;
  }

dd51b6c42c7bc11d29ad9503ef758a78.png

300c6acbfede5fb0f98e55725307f162.png



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