天天看點

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

這裡值得一提的是,這裡有兩個可以優化的地方:

  1. 這裡的GetMonthDay()函數會被反複調用,且monthDay數組始終不變,是以建議定義成static靜态的
  2. 這裡的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)函數:

【C++初階】類和對象修煉下

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;
  }      
【C++初階】類和對象修煉下

然後我們來實作一下,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月的轉變的時候,年份也要變

【C++初階】類和對象修煉下

日期-=天數:

//日期-=天數
  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;
  }