• 【c++】——类和对象(中)——实现完整的日期类(优化)万字详细解疑答惑


    作者:chlorine

    专栏:c++专栏

    赋值运算符重载(+)(+=)(++):实现完整的日期类(上)

    我走的很慢,但我从不后退。

    【学习目标】

    • 日期(- -= --)天数重载运算符
    • 日期-日期 返回天数
    • 对日期类函数进行优化(不符合常理的日期,负数,const成员)
    • c++中重载输入cin和输出cout运算符
    • const成员

    首先我要对上面一篇类和对象(上)进行补充,上一篇主要内容

    • '=' 赋值运算符重载
    • '<'  '='  '>'  '<='  '>='  '!=' 重载运算符
    • '前置++' '后置++’ 重载运算符

    相信大家对于operator的运用以及重载运算符有了一定的了解。本次博客我会继续深化赋值运算符重载的内容,并对各函数进行优化处理。


    目录

    🔑'—' '-='重载运算符

    🔑前置--,后置-- 

     🔑日期-日期=天数

    🔑+=(-=)负数 (优化)

    🔑C++中重载输入cin和输出cout运算符

    🌈重载输入cin运算符>> 

    🌈重载输入cout运算符<<

    🔑不符合常理的日期(优化)

    🔑const成员(优化)

    🌈const对于+=和+ 

    🌈const对于d1d2

    🕶️完整代码

    🚩Date.h

    🚩Date.cpp

    🚩test.cpp


    🔑'—' '-='重载运算符

    我们不只局限于对未来的计算,虽然以前的日子已经一去不复返。如果我们想知道100天是哪一天?该如何操作呢?上面的图片今天是2023年11月18日,一百天前是2023年8月10日,这是该如何计算呢?

    11月已经被-100了,这里要+上的是10月份的天数。

    计算过程如图所示。我们可以根据上面思路进行写代码。

    主要思路:先将日-100,然后如果是负数就进入循环 ,月份-1,就可以得到在此基础上的上一个月的天数,然后我们还要考虑一个情况,就是如果月份=0,我们就要追溯到上一年的12月开始。代码如下:

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

     那么-重载运算符和+重载运算符一个道理,不能改变原来的值,就创建一个临时变量tmp,保留d1原来的值,然后返回tmp.

    依旧使用-复用-=的方法.

    1. Date Date::operator-(int day)
    2. {
    3. Date tmp(*this);
    4. tmp -=day;
    5. return tmp;
    6. }

    🔑前置--,后置-- 

    前置--后置--和前置++后置++的用法是一样的

    • 前置--:返回的减之后的值。
    • 后置--:返回的减之前的值,需要用拷贝构造创建 一个临时变量tmp来保存原来的值。并且后置--重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递  。
    1. //前置--返回的是减之后的值
    2. Date& Date::operator--()
    3. {
    4. *this -= 1;
    5. return *this;
    6. }
    7. //后置--返回的是减之前的值
    8. Date Date::operator--(int)
    9. {
    10. Date tmp = *this;
    11. *this -= 1;
    12. return tmp;
    13. }

     🔑日期-日期=天数

    我想计算一下自己的生日和今天相差多少天?我该如何用代码实现呢?

     第一种思路:

    • 我们可以与小的日期和大的日期的年份设置成一样的,然后就可以利用 《月和日对齐,利用年判断润平年,闰年一年多少天,平年一年的多少天,相加)
    • 然后算出了2003-10-29~~~2023-10-29相差多少天,然后 《年对齐,利用获得每个月的天数进行相加》

    思路的俩个步骤,比较于直接减方便多了。

    第二种思路:

    • d1-d2   我们先假设d1是小min日期,d2是大max日期,如果我的猜测不对,就将min赋值给d2,max赋值给d1。我们要用flag=1或-1来记录,如果(小-大就得乘以-1)(大-小就得乘以1)
    • 小的数如果一直++是可以与大的数持平的,我们就进行一个循环直到min=max的时候结束,过程中min一直++,用n来记下min一直+的次数,n的值就是天数。

    代码如下:

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

    计算机的效率是可以完成循环多次的。 


    🔑+=(-=)负数 (优化)

    图中不管是+还是-都是成立的对于day是正是负都是可以计算的。 

    day+= -100;

     大家有没有想过如果day是负数呢?上面的代码运行的结果是什么呢?

    这时候我们该如何优化呢?

     d1+= -day 是不是相当于 d1-=day?   那么就可以复用-=重载运算符。

    这样对嘛,d1+= -100,本来是看以前的日子的,怎么算到2024了?这里我们就要考虑到数学的知识了。 *this是d1 ,d1-=day ,day是-100,那么负负得正, 就成了d1+=day,所以我们需要在day前面加个负号,即d1-= -day。 -day=100,所以 d1-=100 与 d1+=-100 等式成立。

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

    ​ 

    同理:d1-= -100,看100天后的日期,那么就相当于负负得正,d1+=100,那么用到-=函数里就是  d1+=-day.

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

     


    🔑C++中重载输入cin和输出cout运算符

    c++系统实现了一个庞大的类的库,如下图所示。其中ios为基类,其他类都直接或间接派生自ios类.

    • cin和cout可以直接输入和输出内置类型(如int、double等)和部分标准自定义类型(如string等),原因是标准库已经将所有这些类型的输入和输出重载了,直接使用即可。

    •  对于我们自定义的类型,如果想直接使用cin、cout来输入输出,需要自己重载>>和<<,否则不能直接使用。

    在C++中,标准库本身已经对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数据的输入输出,但是输入输出的对象只能是 C++内置的数据类型(例如 bool、int、double 等)和标准库所包含的类类型(例如 string、complex、ofstream、ifstream 等)。如果自己定义了一种新的数据类型,需要用输入输出运算符去处理,那么就必须对它们进行重载。本节以Date类为例来演示输入输出运算符的重载。

    1. cout << _year << "-" << _month << "-" << _day << endl;//输出形式
    2. cin>>_year>>_month>>_day; //输入形式

    cout是ostream类的对象,cin是istream类的对象,要想达到这个目标,就必须以全局函数(友元函数)的形式重载<<和>>,否则就要修改标准库中的类,这显然不是我们所期望的。

    🌈重载输入cin运算符>> 

    全局函数的形式重载>>,

    1. //cin重载
    2. istream& operator>>(istream& _in, Date& d)
    3. {
    4. _in >> d._year >> d._month >> d._day;
    5. return _in;
    6. }

    注意:运算符重载函数中用到了Date类的private 成员变量,必须在Date类中将该函数声明为友元函数,如下例所示:

    friend istream & operator>> (istream &_in,Date &d);


    🌈重载输入cout运算符<<

    同样地,也可以模仿上面的形式对输入运算符>>进行重载,让它能够输出,请看下面的代码:

    1. //cout重载
    2. ostream& operator<<(ostream& _out, const Date& d)
    3. {
    4. _out << d._year << "-" << d._month << "-" << d._day << endl;
    5. return _out;
    6. }

    ostream表示输出流,cout是ostream类的对象。由于采用了引用的方式进行参数传递,并且也返回了对象的引用,所以重载后的运算符可以实现连续输出。

    为了能够直接访问Date类的private成员变量,同样需要将该函数声明为complex类的友元函数
    friend ostream & operator<< (ostream &out, complex &A);


    友元函数的声明:


    我们有没有观察到

    ​ 

    ​ 

    为什么输入重载里面的参数不加,而输出相反。

    ——输入年月日,如果输入流不能修改,那么就没办法进行输入了,输出在输入之后了,它保留着日期,是不能被修改的。

    在这之前加coonst也是编译不过的,因为流入和流出的过程取它们其中的值也是需要改变它的状态值,所以输出输入都是不能加const的。 提取完了之后要改变其中内部的状态值的。

    输入输出重载运算符代码如下:

    1. //cin重载
    2. istream& operator>>(istream& _in, Date& d)
    3. {
    4. _in >> d._year >> d._month >> d._day;
    5. return _in;
    6. }
    7. //cout重载
    8. ostream& operator<<(ostream& _out, const Date& d)
    9. {
    10. _out << d._year << "-" << d._month << "-" << d._day << endl;
    11. return _out;
    12. }


     我们继续优化吧~


    🔑不符合常理的日期(优化)

    这个日期符合常理嘛?显然是不符合的。所有的对象是构造出来的,所以我们再构造的时候加一些检查~

    • 第一种:直接打印出“非法日期”
    • 第二种:直接断言报错(暴力法)

    既然输出流对于不合理的日期进行了检查,那么输入呢?

    输入非法日期之后,是可以直接通过的,那我们就得给流输入进行检查。

    1. //cin重载
    2. istream& operator>>(istream& _in, Date& d)
    3. {
    4. //第一种写法
    5. _in >> d._year >> d._month >> d._day;
    6. if (d._month > 0 && d._month < 13
    7. && d._day>0 && d._day <= d.GetMonthDay(d._year, d._month))
    8. {
    9. }
    10. else
    11. {
    12. //cout << "非法日期" << endl;
    13. assert(false);
    14. }
    15. return _in;
    16. //第二种写法:
    17. int year, month, day;
    18. _in >> year >> month >> day;
    19. if (month > 0 && month < 13
    20. && day>0 && day <= d.GetMonthDay(year, month))
    21. {
    22. d._year = year;
    23. d._month = month;
    24. d._day = day;
    25. }
    26. else
    27. {
    28. //cout << "非法日期" << endl;
    29. assert(false);
    30. }
    31. }

    一共俩种写法,第二种更灵活。


    🔑const成员(优化)

    const 修饰的 成员函数 称之为 const 成员函数 const 修饰类成员函数,实际修饰该成员函数 隐含的 this 指针 ,表明在该成员函数中 不能对类的任何成员进行修改。

    如何让权限平移或者缩小呢?我们只需要给Date*this改成const Date*this就可以了。

    我们不能动this的类型,因为它是隐含的是无法动的,那么如何将类型改成const使权限不放大呢?所以祖师爷就直接再函数后面加个const就可以了。

    这里加个const 修饰的是*this。const Date* this我们之前就复习了const,这里const修饰的类型是Date,修饰的内容是*this。

    重点:成员函数后面加const以后,普通和const对象都可以调用(权限的平移和权限的缩小)。


    🌈const对于+=和+ 

    那我们所写的所有成员函数都能加个const嘛?

    ——当然不是,要修改的对象成员变量函数不能加

    比如+=成员函数后面加个const。

    _day===》this->day,const修饰的是this指向的内容,指向的内容都不能修改如何+=呢?——肯定不行。那么+呢?

    +不会改变我自己,我们上面说了成员函数后面加const以后,普通和const对象都可以调用(权限的平移和权限的缩小)。(只要不改变成员变量都可以加)

    所以我们看看还有什么可以加const?


    🌈const对于d1d2?

     

    为什么d1

    这里就考虑到了权限的放大缩小平移问题了,d2是const显然传过去就是权限的放大,当然是不可以的。所以最好的方式就是给这些成员函数都加const。

    结论:只要成员函数内部不修改成员变量,就应该加const,这样const对象和普通对象都可以调用。

    这些成员函数都可以后面加const,因为按上面的结论,就是不修改内部成员变量。


    🕶️完整代码

    🚩Date.h

    1. #pragma once
    2. #include<iostream>
    3. #include<assert.h>
    4. using namespace std;
    5. class Date
    6. {
    7. //友元函数的声明
    8. friend ostream& operator<<(ostream& _out, const Date& d);
    9. friend istream& operator>>(istream& _in, Date& d);
    10. public:
    11. int GetMonthDay(int year, int month)
    12. {
    13. int array[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    14. if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
    15. {
    16. return 29;
    17. }
    18. else
    19. {
    20. return array[month];
    21. }
    22. }
    23. void print() const
    24. {
    25. cout << _year << "-" << _month << "-" << _day << endl;
    26. }
    27. Date(int year = 2003, int month = 10, int day = 5);
    28. Date& operator=(const Date& d);
    29. bool operator==(const Date& d)const;
    30. bool operator>(const Date& d)const;
    31. bool operator>=(const Date& d)const;
    32. bool operator<(const Date& d)const;
    33. bool operator<=(const Date& d)const;
    34. bool operator!=(const Date& d)const;
    35. //日期+天数
    36. Date operator+(int day) const;
    37. Date& operator+=(int day);
    38. // 日期-天数
    39. Date operator-(int day)const;//自己不能改变。但是返回之后的结果
    40. Date& operator-=(int day);
    41. //前置++
    42. Date&operator++();
    43. //后置++
    44. Date operator++(int);
    45. //前置--
    46. Date& operator--();
    47. //后置--
    48. Date operator--(int);
    49. //日期-日期
    50. int operator-(const Date& d)const;
    51. private:
    52. int _year;
    53. int _month;
    54. int _day;
    55. };

    🚩Date.cpp

    1. #include<iostream>
    2. using namespace std;
    3. #include"Date.h"
    4. Date::Date(int year, int month, int day)
    5. {
    6. if (month>0&&month<13
    7. &&day>0&&day<=GetMonthDay(year,month))
    8. {
    9. _year = year;
    10. _month = month;
    11. _day = day;
    12. }
    13. else
    14. {
    15. //cout << "非法日期" << endl;
    16. assert(false);
    17. }
    18. }
    19. //赋值运算符重载
    20. Date& Date::operator=(const Date& d)
    21. {
    22. _year = d._year;
    23. _month = d._month;
    24. _day = d._day;
    25. return (*this);
    26. }
    27. bool Date::operator==(const Date& d)const
    28. {
    29. return _year == d._year
    30. && _month == d._month
    31. && _day == d._day;
    32. }
    33. bool Date::operator>(const Date& d)const
    34. {
    35. if (_year > d._year)
    36. {
    37. return true;
    38. }
    39. else if (_year == d._year && _month > d._month)
    40. {
    41. return true;
    42. }
    43. else if (_year == d._year && _month == d._month && _day >d._day)
    44. {
    45. return true;
    46. }
    47. else
    48. {
    49. return false;
    50. }
    51. }
    52. bool Date::operator>=(const Date& d)const
    53. {
    54. return *this > d || *this == d;
    55. }
    56. bool Date::operator<(const Date& d)const
    57. {
    58. return !(*this>=d);
    59. }
    60. bool Date::operator<=(const Date& d)const
    61. {
    62. return !(*this > d);
    63. }
    64. bool Date::operator!=(const Date& d)const
    65. {
    66. return !(*this == d);
    67. }
    68. +=复用+
    69. //Date& Date::operator+=(int day)
    70. //{
    71. // *this = *this + day;
    72. // return *this;
    73. //}
    74. //
    75. //Date Date::operator+(int day)
    76. //{
    77. // Date tmp(*this);
    78. // tmp._day += day;
    79. // while (tmp._day > GetMonthDay(tmp._year, tmp._month))
    80. // {
    81. // tmp._day -= GetMonthDay(tmp._year, tmp._month);
    82. // ++tmp._month;
    83. // if (tmp._month >= 13)
    84. // {
    85. // ++tmp._year;
    86. // tmp._month = 1;
    87. // }
    88. // }
    89. // return tmp;
    90. //}
    91. //+复用+=
    92. Date Date::operator+(int day)const
    93. {
    94. Date tmp(*this);
    95. tmp += day;
    96. return tmp;
    97. }
    98. //d1+=100
    99. Date& Date::operator+=(int day)
    100. {
    101. if (day < 0)
    102. {
    103. return *this -= -day;
    104. }
    105. _day += day;
    106. while (_day > GetMonthDay(_year, _month))
    107. {
    108. _day -= GetMonthDay(_year, _month);
    109. ++_month;
    110. if (_month >= 13)
    111. {
    112. ++_year;
    113. _month = 1;
    114. }
    115. }
    116. return *this;
    117. }
    118. //前置++返回的是加之后的值
    119. Date &Date::operator++()
    120. {
    121. *this += 1;
    122. return *this;
    123. }
    124. //后置++返回的是加之前的值
    125. Date Date::operator++(int)
    126. {
    127. Date tmp = *this;
    128. *this += 1;
    129. return tmp;
    130. }
    131. //前置--返回的是减之后的值
    132. Date& Date::operator--()
    133. {
    134. *this -= 1;
    135. return *this;
    136. }
    137. //后置--返回的是减之前的值
    138. Date Date::operator--(int)
    139. {
    140. Date tmp = *this;
    141. *this -= 1;
    142. return tmp;
    143. }
    144. Date Date::operator-(int day)const
    145. {
    146. Date tmp(*this);
    147. tmp -=day;
    148. return tmp;
    149. }
    150. Date& Date::operator-=(int day)
    151. {
    152. if (day < 0)
    153. {
    154. return *this += -day;
    155. }
    156. _day -= day;
    157. while (_day <= 0)
    158. {
    159. --_month;
    160. if (_month== 0)
    161. {
    162. _month = 12;
    163. --_year;
    164. }
    165. _day+= GetMonthDay(_year, _month);
    166. }
    167. return *this;
    168. }
    169. int Date::operator-(const Date& d)const
    170. {
    171. Date max = *this;
    172. Date min = d;
    173. int flag = 1;
    174. if (*this<d)
    175. {
    176. min = *this;
    177. max = d;
    178. flag = -1;
    179. }
    180. int n = 0;
    181. while (min != max)
    182. {
    183. ++min;
    184. ++n;
    185. }
    186. return n*flag;
    187. }
    188. //cin重载
    189. istream& operator>>(istream& _in, Date& d)
    190. {
    191. //第一种写法
    192. _in >> d._year >> d._month >> d._day;
    193. if (d._month > 0 && d._month < 13
    194. && d._day>0 && d._day <= d.GetMonthDay(d._year, d._month))
    195. {
    196. }
    197. else
    198. {
    199. //cout << "非法日期" << endl;
    200. assert(false);
    201. }
    202. return _in;
    203. //第二种写法:
    204. int year, month, day;
    205. _in >> year >> month >> day;
    206. if (month > 0 && month < 13
    207. && day>0 && day <= d.GetMonthDay(year, month))
    208. {
    209. d._year = year;
    210. d._month = month;
    211. d._day = day;
    212. }
    213. else
    214. {
    215. //cout << "非法日期" << endl;
    216. assert(false);
    217. }
    218. }
    219. //cout重载
    220. ostream& operator<<(ostream& _out, const Date& d)
    221. {
    222. _out << d._year << "-" << d._month << "-" << d._day << endl;
    223. return _out;
    224. }

    🚩test.cpp

    1. #include"Date.h"
    2. void TestDate1()
    3. {
    4. Date d1(2003, 10, 5);
    5. Date d2(2003, 11, 5);
    6. int ret=d1 > d2;
    7. int ret2 = d1 == d2;
    8. int ret3 = d1 < d2;
    9. int ret4 = d1 >= d2;
    10. cout << ret3 << endl;
    11. }
    12. void TestDate2()
    13. {
    14. Date d1(2003, 11, 14);
    15. Date d2=d1 +100;//运用拷贝构造创建一个临时变量tmp,将*this的值给tmp,*this的值不改变,tmp值改变,
    16. //从而导致了d1+day,d1不变,tmp改变。返回d1原来的值。
    17. //d1 +=100;
    18. //d1 += 100;
    19. d2.print();
    20. /*d1++;
    21. d1.print();
    22. ++d1;
    23. d1.print();*/
    24. }
    25. void TestDate3()
    26. {
    27. Date d1(2023, 11, 17);
    28. Date d2=d1 -100;
    29. d2.print();
    30. }
    31. void TestDate4()
    32. {
    33. Date d1(2023, 11, 17);
    34. //d1--;//后置--返回的是之前的值、
    35. --d1;//前置--返回的是之后的值
    36. d1.print();
    37. }
    38. void TestDate5()
    39. {
    40. Date d1(2023, 11, 17);
    41. Date d2(2003, 10, 29);
    42. cout << d1-d2<< endl;
    43. cout << d2 - d1<< endl;
    44. }
    45. void TestDate6()
    46. {
    47. Date d1(2023, 11, 17);
    48. d1.print();
    49. /*d1 += -100;
    50. d1.print();*/
    51. d1 -= -100;
    52. d1.print();
    53. }
    54. void TestDate7()
    55. {
    56. //Date d1(2023, 11, 17);
    57. //d1 += 100;
    58. 流插入
    59. //cout << d1;
    60. //operator<<(cout, d1);
    61. Date d1(2023, 11, 17);
    62. Date d2(2003, 10, 29);
    63. cin >> d1 >> d2;
    64. cout << d1 << d2;
    65. /*Date d4(2023, 13, 1);
    66. cout << d4;*/
    67. }
    68. void TestDate8()
    69. {
    70. Date d4(2023,13,1);
    71. cin>>d4;
    72. cout << d4;
    73. }
    74. void TestDate9()
    75. {
    76. Date d1(2023, 11, 17);
    77. const Date d2(2023, 11, 17);
    78. d1 < d2;
    79. d2 < d1;
    80. //d1 + 100;//d1普通对象
    81. //d2 + 100;//const对象
    82. }
    83. int main()
    84. {
    85. //TestDate1();
    86. //TestDate2();
    87. //TestDate3();
    88. //TestDate4();
    89. //TestDate5();
    90. //TestDate6();
    91. //TestDate7();
    92. //TestDate8();
    93. TestDate9();
    94. return 0;
    95. }

    我走的很慢,但我从不后退。

  • 相关阅读:
    数据结构之:数组
    管理上游数据源配置
    使用Nginx在 Linux 上托管 ASP.NET Core 6.0应用:GitHub Actions自动部署
    python数据分析(3)
    Java中IO、BIO、NIO、AIO分别是什么?
    Error注入攻击
    后端技术盲区大清理:事务还没弄明白的小伙伴赶紧来看一看
    02 认识Verilog HDL
    Shell编程_0Linux之RPM和YUM
    【ONE·C++ || 网络基础(二)】
  • 原文地址:https://blog.csdn.net/m0_74438843/article/details/134479385