• 类和对象进阶------构建日期类


    目录

    一、日期类的构造

    二、日期类的比较运算符重载

    三、日期类的加减运算符重载

    四、日期相减

    五、const成员

    六、日期类重载流输入输出 


    一、日期类的构造

    首先,日期类不能没有年月日,因此需要年月日参数,我们int一下这三个参数,我们不希望外界直接访问这三个参数,因此设置为private,同时我们需要自己写一个拷贝构造来对参数进行初始化。这里使用了全缺省,同时为了更加贴合实战项目的要求,我们将声明写到头文件Date.h里,将定义写到Date.cpp里,来进行声明和定义分离。

    同时为了保证我们传入的参数是合法的,我们在拷贝构造函数里可以添加判断,先写出一个  GetMonthDay  类函数来获取这一年的这一月有多少天

    Date.h的声明

    1. class Date
    2. {
    3. public:
    4. Date(int year = 1, int month = 1, int day = 1);
    5. int GetMonthDay(int year,int month);
    6. private:
    7. int _year;
    8. int _month;
    9. int _day;
    10. };

    我们在Date.cpp写了定义。构造函数赋值并判断是否合法  GetMonthDay  能返回这一年的这个月有几天

    1. Date::Date(int year, int month, int day)
    2. {
    3. _year = year;
    4. _month = month;
    5. _day = day;
    6. if (month < 1 || month>12 || day<1 || day>GetMonthDay(year, month))
    7. {
    8. cout << "非法日期" << endl;
    9. }
    10. }
    11. int Date::GetMonthDay(int year, int month)
    12. {
    13. static int MonthDay[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    14. if (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))
    15. {
    16. return 29;
    17. }
    18. return MonthDay[month];
    19. }

    注意:缺省参数一定要写在声明的地方,定义的地方不能写缺省参数。 

    二、日期类的比较运算符重载

     我们想要一个能简便的对比两个日期类对象的大小,也就是两个日期比较大小,由于是自定义类型不能直接比较,因此我们要进行函数重载,在Date.h文件类里面添加上如下定义

    bool operator<(const Date& d);

    同时在Date.cpp添加上函数实现,注意一定要在函数名前加上  Date::  来表示是那个作用域下的,代码部分就是简单的作比较,先比较年,再比较月,最后比较天是最符合逻辑的。

    1. bool Date::operator<(const Date& d)
    2. {
    3. if (_year < d._year) {
    4. return true;
    5. }
    6. else if (_year == d._year && _month < d._month) {
    7. return true;
    8. }
    9. else if (_year == d._year && _month == d._month && _day < d._day) {
    10. return true;
    11. }
    12. else {
    13. return false;
    14. }
    15. }

    到目前我们已经重载了  <   符号,还有其他的判断符号需要重载,都很简单,我就直接代码了 

    声明:

    1. bool operator<(const Date& d);
    2. bool operator==(const Date& d);
    3. bool operator<=(const Date& d);
    4. bool operator>(const Date& d);
    5. bool operator>=(const Date& d);
    6. bool operator!=(const Date& d);

    定义:

    1. bool Date::operator<(const Date& d)
    2. {
    3. if (_year < d._year) {
    4. return true;
    5. }
    6. else if (_year == d._year && _month < d._month) {
    7. return true;
    8. }
    9. else if (_year == d._year && _month == d._month && _day < d._day) {
    10. return true;
    11. }
    12. else {
    13. return false;
    14. }
    15. }
    16. bool Date::operator==(const Date& d)
    17. {
    18. return _year == d._year && _month == d._month && _day == d._day;
    19. }
    20. bool Date::operator<=(const Date& d)
    21. {
    22. return *this < d || *this == d;
    23. }
    24. bool Date::operator>(const Date& d)
    25. {
    26. return !(*this <= d);
    27. }
    28. bool Date::operator>=(const Date& d)
    29. {
    30. return !(*this < d);
    31. }
    32. bool Date::operator!=(const Date& d)
    33. {
    34. return !(*this == d);
    35. }

    我们写个日期类打印函数测试一下

    Date.h 里面声明

    void Print();

    Date.cpp里面定义 

    1. void Date::Print()
    2. {
    3. cout << _year << "/" << _month << "/" << _day << endl;
    4. }

    已经可以完成Date类对象的比较操作了 

    三、日期类的加减运算符重载

    我们还想做到运算这个日期加上天数是什么日期。

    例如今天是2023年10月10号,我想要知道300天后是几几年多少月多少号,就可以重载加法运算符来帮助我们计算。

    首先要直接将要加的天数先加到当前类对象的_day,算出到了这个月的多少天,在判断这个天数有没有越阶,如果天数不符合,就将天数减去这个月的天数,同时月份++,如果月份也溢出了,到了13月,就将月份置为1,年再加加。注意返回值为 *this 因为是+=,返回的就是类对象本身,同时由于函数结束后, *this 依然存在,因此可以穿&(引用返回)。 

    同时,day有可能为负数,那就相当于减day天,因此我们还要加个判断 day大于等于0走上面的代码,小于0可以复用上-=。我们只需把-=完善一下即可

    1. if (day >= 0)
    2. {
    3. this->_day += day;
    4. while (_day > GetMonthDay(_year,_month))
    5. {
    6. _day -= GetMonthDay(_year, _month);
    7. _month++;
    8. if (_month == 13)
    9. {
    10. _month = 1;
    11. _year++;
    12. }
    13. }
    14. }
    15. else
    16. {
    17. *this -= (-day);
    18. }
    19. return *this;

    -=和+=类似的,唯一的区别是要区分出去找哪个月借,这里是先将_month--  之后,在去让_day+=月的天数,因为你当前day是负数后,你需要找前一个月去借天数,直到大于0才对。

    1. Date& Date::operator-=(int day)
    2. {
    3. if (day >= 0)
    4. {
    5. this->_day -= day;
    6. while (_day <= 0)
    7. {
    8. _month--;
    9. _day += GetMonthDay(_year, _month);
    10. if (_month == 0)
    11. {
    12. _month = 12;
    13. _year--;
    14. }
    15. }
    16. }
    17. else
    18. {
    19. *this += (-day);
    20. }
    21. return *this;
    22. }

     日期类的+=和-=我们已经处理好了,还有+和-没有重载,我们也可以直接服用,拷贝构造一个tmp类对象,复用+=和-=,再return这个临时对象即可。

    注意:返回值不能加&(引用),因为tmp的生命周期是在这个函数内部,出了函数就销毁, 因此不能加&

    1. Date Date::operator+(int day)
    2. {
    3. Date tmp(*this);
    4. tmp += day;
    5. return tmp;
    6. }
    7. Date Date::operator-(int day)
    8. {
    9. Date tmp(*this);
    10. tmp -= day;
    11. return tmp;
    12. }

    Date.h文件填上就好,后续就不多赘述了,在最后面也会附上代码。

    运算符重载还有++和--

    这里会产生一个问题,按照之前的思路来重载++运算符,你无法区分这个++是前置还是后置,C++祖师爷添加了一个参数来区分前置还是后置,没有参数的是前置++,有int参数的是后置++,这个int可以不给值,他只是一个占位符,来表示后置++。

    同理  --  也是一样的。

    这里我们也可以复用之前的+=来帮我们处理,前置++返回的是++后的值,直接返回 *this,因此可以用&返回。

    后置++返回的是++前的值, 因此需要拷贝构造一份tmp,返回tmp即可,tmp出了函数就销毁了不能用&返回。

    注意:对于类对象来说,前置++没有产生拷贝构造,而后置++产生了拷贝构造,因此如果只是单条语句用前置++效率会更高

    1. Date& Date::operator++()
    2. {
    3. *this += 1;
    4. return *this;
    5. }
    6. Date Date::operator++(int)
    7. {
    8. Date tmp(*this);
    9. *this += 1;
    10. return tmp;
    11. }
    12. Date& Date::operator--()
    13. {
    14. *this -= 1;
    15. return *this;
    16. }
    17. Date Date::operator--(int)
    18. {
    19. Date tmp(*this);
    20. *this -= 1;
    21. return tmp;
    22. }

    四、日期相减

    日期减日期,可以找到这两个日期之前相差多少天,是日期加天数的变形。

    这里采用了一个很简单的方法,就是先找到两个日期小的那个,再将小的日期一步一步加到大的日期,加了多少次,就有多少天。

    flag既可以判断谁大,又可以作为符号,因为是相减,有可能是小的日期减去大的日期,这样结果是一个负数。

    1. int Date::operator-(const Date& d)
    2. {
    3. int flag = 1;
    4. Date max = *this;
    5. Date min = d;
    6. if (max < min)
    7. {
    8. flag = -1;
    9. max = d;
    10. min = *this;
    11. }
    12. int n = 0;
    13. while (max != min)
    14. {
    15. ++min;
    16. ++n;
    17. }
    18. return n * flag;
    19. }

    再运行一下检查

     

    跟网上的日期计算器作对比,没问题~。

    五、const成员

    对于一个参数有类对象的函数,如果我们不需要在函数里面进行修改操作,那么我们可以给类对象添加上const修饰,使其无法修改,这样操作会更加安全,但是这有会引发一些问题,例如类对象调用不了类的成员函数

    为什么会发生上图这种情况?

    因为这里的d是一个const修饰的类对象,而  Print()  函数是一个普通的成员函数,没有被const修饰,这就会引发权限放大的问题,由const转为非const,是不能够实现的,即使我们在  Print()   函数例没有进行任何修改。

    那么似乎我们只需要给类对象的成员函数加上  const  修饰一下即可,但是我们又发现函数实现并没有传任何参数,只有一个默认传的this指针,而this指针我们既不需要传给函数,又不能够穿给函数,这似乎是一个悖论,那么我们该如何让这个指针加上const呢?

    C++祖师爷想了一个很好的方法,在 Print() 后面添加上const来代表传递的this指针为const,如下

    这个时候我们回过头来看,就会发现代码不再报错了,也可以正常调用了。 

    再验证一下普通类对象能不嫩调用  const函数,可以发现也是没问题的。

    同理,如果函数是只读函数(内部不涉及修改成员的都是只读函数),就可以加const。如下

    1. bool operator<(const Date& d) const;
    2. bool operator==(const Date& d) const;
    3. bool operator<=(const Date& d) const;
    4. bool operator>(const Date& d) const;
    5. bool operator>=(const Date& d) const;
    6. bool operator!=(const Date& d) const;
    7. Date& operator+=(int day);
    8. Date& operator-=(int day);
    9. Date operator+(int day) const;
    10. Date operator-(int day) const;
    11. Date& operator++();
    12. Date operator++(int);
    13. Date& operator--();
    14. Date operator--(int);
    15. //日期相减
    16. int operator-(const Date& d) const;
    17. void Print() const;
    18. int GetMonthDay(int year,int month) const;

    const对象不可以调用非const成员函数(权限的扩大)

    非const对象可以调用const成员函数   (权限的缩小)

    权限可以平移,可以缩小,但是不能放大

    对于类对象来说,再后面加了const函数和不加const函数是构成重载的,因为他们默认给到的this指针类型不同。因此有时候会需要我们写两个函数,一个是const的一个是非const的,如下 

     

    不过这里我们可以不需要重载,没涉及到那么复杂的情景,加了const就足够了。

    六、日期类重载流输入输出 

    我们想让日期类对象和普通的内置类型一样可以用 cin和 cout 来处理,就得重载>>和 << 这两个操作符,如果我们将重载 >> 函数放在类的里面是不可行的,如下

    1. ostream& Date::operator<<(ostream& out)
    2. {
    3. out << _year << "/" << _month << "/" << _day << endl;
    4. return out;
    5. }

    我们明明重载了,为啥会报链接错误呢?因为this指针默认在第一个参数。而out是第二个参数,这和我们的代码  cout << d1; 不匹配,要交换位置才能运行,因此我们不能够这样写。

    这里我们采取的方法是写出全局函数,用友元的形式去访问类的私有变量。 将 类对象放在第二个参数的位置就能符合我们的要求。同时也重载一下 >> 流输入,只需要关注流输入的类对象d不要加const修饰就行,因为我们要修改这个对象,因此不能加const。

    1. friend ostream& operator<<(ostream& out, const Date& d);
    2. friend istream& operator>>(istream& in, Date& d);
    1. ostream& operator<<(ostream& out, const Date& d)
    2. {
    3. out << d._year << "/" << d._month << "/" << d._day << endl;
    4. return out;
    5. }
    6. istream& operator>>(istream& in, Date& d)
    7. {
    8. in >> d._year >> d._month >> d._day;
    9. return in;
    10. }

    如此一来,代码就大功告成了

    附上全部代码

    Date.h

    1. #pragma once
    2. #include
    3. using namespace std;
    4. class Date
    5. {
    6. friend ostream& operator<<(ostream& out, const Date& d);
    7. friend istream& operator>>(istream& in, Date& d);
    8. public:
    9. Date(int year = 1, int month = 1, int day = 1);
    10. bool operator<(const Date& d) const;
    11. bool operator==(const Date& d) const;
    12. bool operator<=(const Date& d) const;
    13. bool operator>(const Date& d) const;
    14. bool operator>=(const Date& d) const;
    15. bool operator!=(const Date& d) const;
    16. Date& operator+=(int day);
    17. Date& operator-=(int day);
    18. Date operator+(int day) const;
    19. Date operator-(int day) const;
    20. Date& operator++();
    21. Date operator++(int);
    22. Date& operator--();
    23. Date operator--(int);
    24. int operator-(const Date& d) const;
    25. void Print() const;
    26. int GetMonthDay(int year,int month) const;
    27. private:
    28. int _year;
    29. int _month;
    30. int _day;
    31. };
    32. ostream& operator<<(ostream& out, const Date& d);
    33. istream& operator>>(istream& in, Date& d);

     Date.cpp

    1. #include"Date.h"
    2. Date::Date(int year, int month, int day)
    3. {
    4. _year = year;
    5. _month = month;
    6. _day = day;
    7. if (month < 1 || month>12 || day<1 || day>GetMonthDay(year, month))
    8. {
    9. cout << "非法日期" << endl;
    10. }
    11. }
    12. bool Date::operator<(const Date& d) const
    13. {
    14. if (_year < d._year) {
    15. return true;
    16. }
    17. else if (_year == d._year && _month < d._month) {
    18. return true;
    19. }
    20. else if (_year == d._year && _month == d._month && _day < d._day) {
    21. return true;
    22. }
    23. else {
    24. return false;
    25. }
    26. }
    27. bool Date::operator==(const Date& d) const
    28. {
    29. return _year == d._year && _month == d._month && _day == d._day;
    30. }
    31. bool Date::operator<=(const Date& d) const
    32. {
    33. return *this < d || *this == d;
    34. }
    35. bool Date::operator>(const Date& d) const
    36. {
    37. return !(*this <= d);
    38. }
    39. bool Date::operator>=(const Date& d) const
    40. {
    41. return !(*this < d);
    42. }
    43. bool Date::operator!=(const Date& d) const
    44. {
    45. return !(*this == d);
    46. }
    47. Date& Date::operator+=(int day)
    48. {
    49. if (day >= 0)
    50. {
    51. this->_day += day;
    52. while (_day > GetMonthDay(_year,_month))
    53. {
    54. _day -= GetMonthDay(_year, _month);
    55. _month++;
    56. if (_month == 13)
    57. {
    58. _month = 1;
    59. _year++;
    60. }
    61. }
    62. }
    63. else
    64. {
    65. *this -= (-day);
    66. }
    67. return *this;
    68. }
    69. Date& Date::operator-=(int day)
    70. {
    71. if (day >= 0)
    72. {
    73. this->_day -= day;
    74. while (_day <= 0)
    75. {
    76. _month--;
    77. _day += GetMonthDay(_year, _month);
    78. if (_month == 0)
    79. {
    80. _month = 12;
    81. _year--;
    82. }
    83. }
    84. }
    85. else
    86. {
    87. *this += (-day);
    88. }
    89. return *this;
    90. }
    91. Date Date::operator+(int day) const
    92. {
    93. Date tmp(*this);
    94. /*if (day >= 0)
    95. {
    96. tmp._day += day;
    97. while (tmp._day > GetMonthDay(tmp._year,tmp._month))
    98. {
    99. tmp._day -= GetMonth(tmp._month);
    100. tmp._month++;
    101. if (tmp._month == 13)
    102. {
    103. tmp._month = 1;
    104. tmp._year++;
    105. }
    106. }
    107. }
    108. else
    109. {
    110. *this - (-day);
    111. }*/
    112. tmp += day;
    113. return tmp;
    114. }
    115. Date Date::operator-(int day) const
    116. {
    117. Date tmp(*this);
    118. /*if (day >= 0)
    119. {
    120. tmp._day -= day;
    121. while (tmp._day < 0)
    122. {
    123. tmp._day += GetMonthDay(tmp._year,tmp._month);
    124. tmp._month--;
    125. if (tmp._month == 0)
    126. {
    127. tmp._month = 12;
    128. tmp._year--;
    129. }
    130. }
    131. }
    132. else
    133. {
    134. *this + (-day);
    135. }*/
    136. tmp -= day;
    137. return tmp;
    138. }
    139. Date& Date::operator++()
    140. {
    141. *this += 1;
    142. return *this;
    143. }
    144. Date Date::operator++(int)
    145. {
    146. Date tmp(*this);
    147. *this += 1;
    148. return tmp;
    149. }
    150. Date& Date::operator--()
    151. {
    152. *this -= 1;
    153. return *this;
    154. }
    155. Date Date::operator--(int)
    156. {
    157. Date tmp(*this);
    158. *this -= 1;
    159. return tmp;
    160. }
    161. int Date::operator-(const Date& d) const
    162. {
    163. int flag = 1;
    164. Date max = *this;
    165. Date min = d;
    166. if (max < min)
    167. {
    168. flag = -1;
    169. max = d;
    170. min = *this;
    171. }
    172. int n = 0;
    173. while (max != min)
    174. {
    175. ++min;
    176. ++n;
    177. }
    178. return n * flag;
    179. }
    180. void Date::Print() const
    181. {
    182. cout << _year << "/" << _month << "/" << _day << endl;
    183. }
    184. int Date::GetMonthDay(int year, int month) const
    185. {
    186. static int MonthDay[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    187. if (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))
    188. {
    189. return 29;
    190. }
    191. return MonthDay[month];
    192. }
    193. ostream& operator<<(ostream& out, const Date& d)
    194. {
    195. out << d._year << "/" << d._month << "/" << d._day << endl;
    196. return out;
    197. }
    198. istream& operator>>(istream& in, Date& d)
    199. {
    200. in >> d._year >> d._month >> d._day;
    201. return in;
    202. }

     Test.cpp

    1. #include"Date.h"
    2. int main()
    3. {
    4. Date d1(2023, 10, 10);
    5. Date d2(2000, 2, 16);
    6. cout << d1 << d2;
    7. cin >> d1 >> d2;
    8. cout << d1 << d2;
    9. }

  • 相关阅读:
    高并发使用JVM锁和MySQL锁解决数据不一致问题
    C++之多态详解
    IIS部署.Net 7项目
    多线程案例
    安利一款纯Python编写的GTF处理脚本 - GTFtools
    ffmpeg把RTSP流分段录制成MP4,如果能把ffmpeg.exe改成ffmpeg.dll用,那音视频开发的难度直接就降一个维度啊
    【毕业设计】基于javaEE+原生Servlet+MySql的业务绩效考核系统设计与实现(毕业论文+程序源码)——业务绩效考核系统
    Nginx手动编译、安装超超详解
    20220727NOI模拟赛--考后总结
    【Android】Binder的Oneway拦截
  • 原文地址:https://blog.csdn.net/kkbca/article/details/133706325