• lesson-2C++类与对象(中)


    个人主页:Lei宝啊 

    愿所有美好如期而遇


    目录

    类的6个默认成员函数

    构造函数

    概念

    特性

    析构函数

    概念

    特性

    拷贝构造函数

    概念

    特性

    赋值运算符重载

    运算符重载

    赋值运算符重载

    前置++和后置++重载

    日期类的实现


    类的6个默认成员函数

    如果一个类中什么成员都没有,简称为空类。
    1. class null //null是类名
    2. {
    3. };
    空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
    函数。

    构造函数

    概念

    我们可以先通过一个对象的初始化函数引入。

    1. #include <iostream>
    2. using namespace std;
    3. class Date
    4. {
    5. public:
    6. void Init(int year, int month, int day)
    7. {
    8. _year = year;
    9. _month = month;
    10. _day = day;
    11. }
    12. void Print()
    13. {
    14. cout << _year << "-";
    15. cout << _month << "-";
    16. cout << _day << endl;
    17. }
    18. private:
    19. int _year;
    20. int _month;
    21. int _day;
    22. };
    23. int main()
    24. {
    25. Date a;
    26. a.Init(2023, 10, 20);
    27. a.Print();
    28. return 0;
    29. }

    每一次我们创建出一个对象都要手动为其初始化,如果忘了的话,轻点是随机值,重点程序就崩了,所以我们的构造函数就解决了这个问题。

    构造函数 是一个 特殊的成员函数,名字与类名相同 , 创建类类型对象时由编译器自动调用 ,以保证
    每个数据成员都有 一个合适的初始值,并且 在对象整个生命周期内只调用一次

    看代码。 

    1. #include <iostream>
    2. using namespace std;
    3. class Date
    4. {
    5. public:
    6. //构造函数支持重载
    7. Date()
    8. {
    9. _year = 1;
    10. _month = 1;
    11. _day = 1;
    12. }
    13. Date(int year, int month, int day)
    14. {
    15. _year = year;
    16. _month = month;
    17. _day = day;
    18. }
    19. void Print()
    20. {
    21. cout << _year << "-";
    22. cout << _month << "-";
    23. cout << _day << endl;
    24. }
    25. private:
    26. int _year;
    27. int _month;
    28. int _day;
    29. };
    30. int main()
    31. {
    32. Date a;
    33. a.Print();
    34. return 0;
    35. }

     我们可以看到我们a对象里的年月日确实有值了,被我们所写的的默认构造函数所调用,并全部赋值为1.

    接下来我们试试有参数的构造函数。

    1. int main()
    2. {
    3. Date a(2023,10,21);
    4. a.Print();
    5. return 0;
    6. }

    我们发现编译器调用的是我们有参数的构造函数。

    特性

    构造函数 是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
    并不是开空间创建对象,而是初始化对象
    特征 如下:
    1. 函数名与类名相同。
    2. 无返回值。
    3. 对象实例化时编译器 自动调用 对应的构造函数。
    4. 构造函数可以重载。

     在上述代码中,也体现出了构造函数的这四个特性。

    但是有几点我们要注意

    1:没有参数的构造函数,系统默认的构造函数,全缺省的构造函数,都叫做默认构造函数,而这三个构造函数不可同时写出,任意二者不可同时存在,我们看代码解释。

    1. class Date
    2. {
    3. public:
    4. Date()
    5. {
    6. _year = 1;
    7. _month = 1;
    8. _day = 1;
    9. }
    10. Date(int year = 10, int month = 10, int day = 10)
    11. {
    12. _year = year;
    13. _month = month;
    14. _day = day;
    15. }
    16. private:
    17. int _year;
    18. int _month;
    19. int _day;
    20. };
    21. int main()
    22. {
    23. Date a;
    24. return 0;
    25. }

    因为编译器无法区分到要调用哪个构造函数,所以就报错了。

    2:当我们定义了构造函数后,编译器不会再生成默认构造函数,但这样也会出现一个小问题需要我们去掌控。

    1. class Date
    2. {
    3. public:
    4. Date(int year, int month, int day)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. }
    10. private:
    11. int _year;
    12. int _month;
    13. int _day;
    14. };
    15. int main()
    16. {
    17. Date a;
    18. return 0;
    19. }

    因为我们自定义了一个构造函数,所以系统不会再生成默认构造函数,而我们实例化的对象要调用无参的构造函数,但是我们又没有无参的构造函数,所以编译器就只能报错,我们后面会讲到有解决方法。

    3.如果说我们没有写构造函数,内置类型的成员变量使用系统的默认构造函数,不会做处理,自定义类型的成员变量会去调用他自己的默认构造函数。

    解释:内置类型就是系统自带的,比如int,double之类的,像类和结构体等就是自定义类型。

    1. class Stack
    2. {
    3. public:
    4. Stack(int capacity = 4)
    5. {
    6. _capacity = capacity;
    7. int top = 0;
    8. int* a = (int*)malloc(sizeof(int) * capacity);
    9. }
    10. ~Stack()
    11. {
    12. free(_a);
    13. _top = 0;
    14. _capacity = 0;
    15. }
    16. private :
    17. int* _a;
    18. int _top;
    19. int _capacity;
    20. };
    21. class Queue
    22. {
    23. Stack _a;
    24. Stack _b;
    25. int size;
    26. };
    27. int main()
    28. {
    29. Stack a;
    30. Queue b;
    31. return 0;
    32. }

    细心的朋友们可能会发现size被初始化为0了,你不是说内置类型的变量不初始化吗?是的,但是不同的编译器处理结果不同,我这个是Visual Studio 2022 ,在2013下是不会给初始化的,我们想要我们写出的代码具有跨平台性,就不要寄希望于编译器会给优化,所以我们就当做他不会给优化处理,写出优质代码。

    4:C++11新特性,允许声明成员变量时给默认值。

    1. class Date
    2. {
    3. public:
    4. void Print()
    5. {
    6. cout << _year << _month << _day;
    7. }
    8. private:
    9. int _year = 1;
    10. int _month = 1;
    11. int _day = 1;
    12. };
    13. int main()
    14. {
    15. Date a;
    16. a.Print();
    17. return 0;
    18. }

    析构函数

    概念

    通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?
    析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
    编译器完成的。而 对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
    像我们上面有代码~Stack就是析构函数。

    特性

    析构函数 是特殊的成员函数,其 特征 如下:
    1. 析构函数名是在类名前加上字符 ~
    2. 无参数无返回值类型。
    3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
        注意:析构函数不能重载
    4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数

     我们来测试一下。

    1. class Stack
    2. {
    3. public:
    4. Stack(int capacity = 4)
    5. {
    6. _capacity = capacity;
    7. int top = 0;
    8. int* a = (int*)malloc(sizeof(int) * capacity);
    9. }
    10. ~Stack()
    11. {
    12. free(_a);
    13. _top = 0;
    14. _capacity = 0;
    15. cout << "haha" << endl;
    16. }
    17. private :
    18. int* _a;
    19. int _top;
    20. int _capacity;
    21. };
    22. class Queue
    23. {
    24. Stack _a;
    25. Stack _b;
    26. int size;
    27. };
    28. int main()
    29. {
    30. Queue b;
    31. return 0;
    32. }

    结果显而易见,我们的确是调用了析构函数

    拷贝构造函数

    概念

    拷贝构造函数 只有单个形参 ,该形参是对本 类类型对象的引用 ( 一般常用 const 修饰 ) ,在用 已存
    在的类类型对象创建新对象时由编译器自动调用

    特性

    我们先来写一段代码证明拷贝构造函数的必要性

    首先是日期类的测试

    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. private:
    12. int _year;
    13. int _month;
    14. int _day;
    15. };
    16. void func1(Date a)
    17. {
    18. cout << "func1(Date a)" << endl;
    19. }
    20. int main()
    21. {
    22. Date d1(2023, 10, 22);
    23. func1(d1);
    24. return 0;
    25. }
    注意日期类函数调用的析构函数是系统默认的,而且也没什么需要释放的。
    所以在我们将d1这个对象传给func1这个函数的时候时值传递,当fun1结束时,a这个对象要销毁,会去调用析构函数。
    接下来我们来看栈这个类
    1. class Stack
    2. {
    3. public:
    4. Stack(int capacity)
    5. {
    6. _capacity = capacity;
    7. int top = 0;
    8. _a = (int*)malloc(sizeof(int) * _capacity);
    9. if (_a == nullptr)
    10. {
    11. perror("malloc");
    12. }
    13. cout << "Stack的构造" << endl;
    14. }
    15. ~Stack()
    16. {
    17. free(_a);
    18. _a = nullptr;
    19. _top = 0;
    20. _capacity = 0;
    21. cout << "Stack的析构" << endl;
    22. }
    23. private:
    24. int* _a;
    25. int _top;
    26. int _capacity;
    27. };
    28. void func2(Stack st)
    29. {
    30. //...
    31. }
    32. int main()
    33. {
    34. Stack st1(4);
    35. func2(st1);
    36. return 0;
    37. }

    这个栈类再这么调用就会出问题,当st销毁时,去调用我们所写的析构函数,在func2函数结束时会释放一次_a,当主函数中的对象st1销毁时,会对已经释放的那块空间再释放一次,这样程序就崩溃了,因为此时的_a就是野指针了,别忘了我们是传值,空间释放后_a就成了野指针,释放野指针指向的空间是不合法的,所以就崩了。

    那也许有人会说,我传引用不就好了吗,但是假设我们有一个需求,不改变对象本身,就是要拷贝一份去实现,那么拷贝构造就凸显出作用来了。(如果还有人说栈这个类我用系统默认的析构函数不好吗?那您可真是昏了头了)

    拷贝构造函数也是特殊的成员函数,其 特征 如下:
    1. 拷贝构造函数 是构造函数的一个重载形式
    1. Stack(int capacity)
    2. {
    3. _capacity = capacity;
    4. int top = 0;
    5. _a = (int*)malloc(sizeof(int) * _capacity);
    6. if (_a == nullptr)
    7. {
    8. perror("malloc");
    9. }
    10. cout << "Stack的构造" << endl;
    11. }
    12. //拷贝构造
    13. Stack(const Stack& st)
    14. {
    15. _top = st._top;
    16. _capacity = st._capacity;
    17. _a = (int*)malloc(sizeof(int) * _capacity);
    18. if (_a == nullptr)
    19. {
    20. perror("malloc");
    21. }
    22. }

    2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错
    因为会引发无穷递归调用。
    拷贝构造参数不引用为什么会引发无穷递归呢?
    首先要明白在传值拷贝自定义类型定义出的对象时,编译器会去调用他的拷贝构造,我们传值传参Stack类型的对象,也就是要拷贝一份给参数,所以编译器会去调这个对象的拷贝构造,我们去掉引用后的拷贝构造是这个样子的
    1. Stack(const Stack st)
    2. {
    3. _top = st._top;
    4. _capacity = st._capacity;
    5. _a = (int*)malloc(sizeof(int) * _capacity);
    6. if (_a == nullptr)
    7. {
    8. perror("malloc");
    9. }
    10. }

    接下来我们通过图解来看一看无穷递归的出现原因。

    所以我们要加上&。

    3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
    字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    我们的日期类是不用我们再去单独写拷贝构造函数的,因为系统默认的拷贝构造函数就是值拷贝。
    拷贝构造函数典型调用场景:
    使用已存在对象创建新对象
    Stack st2(st1);
    函数参数类型为类类型对象
    当然,一般来说,没有特别要求,我们还是推荐使用引用的,效率会高些。
    1. void func2(Stack st)
    2. {
    3. //...
    4. }
    函数返回值类型为类类型对象
    1. Date func3(Date d)
    2. {
    3. //如果出了作用域,传值给d的对象还存在,
    4. //那么引用返回更好,这里我们只是为了举例子
    5. //而且我们是推荐传引用的
    6. return d;
    7. }

    返回的时候,d已然销毁,所以我们返回的其实是d的拷贝。

    为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用
    尽量使用引用。

    赋值运算符重载

    这里我们还是会通过一些例子来引入这个概念。

    我们打算写个日期类来解释,在此之前,我们需要先明白,日期和日期的相加减有没有意义,比如

    2023-10-23 + 2023-10-24,显然是没有意义的,但是他们之间相减就有了意义;两个日期相差多少天,那么日期间的乘除也是没有意义的;再看2023-10-23 + 50天,这个显然也是有意义的,计算50天日期是多少;这么说的话,其实日期的比较也是有意义的,相等和大小。

    那么我们如何去实现日期间的比较和日期加减天数呢?

    但是这些成员变量都是私有的,在类外我们不能直接用,当然,我们可以在类里写上GetYear函数等来取得他们的值进行比较,我们这里只是测试,就先将他们公开,当然这样的代码是不健康的,但是我们也说了,只是测试。

    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. int _year;
    12. int _month;
    13. int _day;
    14. };
    15. bool equal(Date d1, Date d2)
    16. {
    17. if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
    18. return true;
    19. else
    20. return false;
    21. }
    22. int main()
    23. {
    24. Date d1;
    25. Date d2(2023, 2, 13);
    26. bool ret = equal(d1, d2);
    27. cout << ret << endl;
    28. return 0;
    29. }

    当然,这样会更好些

    1. bool equal(const Date& d1, const Date& d2)
    2. {
    3. if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
    4. return true;
    5. else
    6. return false;
    7. }

     接下来进行大小比较函数,起个什么名字好呢?compare?比大还是比小?我怎么知道,compare_large? 当下,我们先随便起名字。

    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. int _year;
    12. int _month;
    13. int _day;
    14. };
    15. bool equal(const Date& d1, const Date& d2)
    16. {
    17. if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
    18. return true;
    19. else
    20. return false;
    21. }
    22. bool compare(const Date& d1, const Date& d2)
    23. {
    24. if (d1._year > d2._year)
    25. {
    26. return true;
    27. }
    28. else if (d1._year == d2._year && d1._month > d2._month)
    29. {
    30. return true;
    31. }
    32. else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day)
    33. {
    34. return true;
    35. }
    36. return false;
    37. }
    38. int main()
    39. {
    40. Date d1;
    41. Date d2(2023, 2, 13);
    42. bool ret1 = equal(d1, d2);
    43. bool ret2 = compare(d1, d2);
    44. cout << ret1 << endl;
    45. cout << ret2 << endl;
    46. return 0;
    47. }

     

    我们可以想想内置类型定义出的变量,都是可以直接比较和相加减的,那么我自定义类型定义的对象能不能也直接比较相加减呢?答案是不能,编译器无法理解对象如何比较,比如我们的d1和d2,怎么确定大小呢?年月日都大于才算大于吗?由此,我们引入一个新的概念,运算符重载,不进解决了函数名不便于理解的问题,还可以进行对象的比较。

    运算符重载

    C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其
    返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
    函数名字为:关键字 operator 后面接需要重载的运算符符号
    函数原型: 返回值类型  operator 操作符 ( 参数列表 )
    看代码前别忘了六个成员函数每个函数都有一个隐含的this指针。
    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. //比较相等
    12. bool operator==(const Date& d2)
    13. {
    14. if (_year == d2._year &&_month == d2._month && _day == d2._day)
    15. return true;
    16. else
    17. return false;
    18. }
    19. //比较大于
    20. bool operator>(const Date& d2)
    21. {
    22. if (_year > d2._year)
    23. {
    24. return true;
    25. }
    26. else if (_year == d2._year && _month > _month)
    27. {
    28. return true;
    29. }
    30. else if (_year == d2._year && _month == d2._month && _day > d2._day)
    31. {
    32. return true;
    33. }
    34. return false;
    35. }
    36. private:
    37. int _year;
    38. int _month;
    39. int _day;
    40. };
    41. int main()
    42. {
    43. Date d1;
    44. Date d2(2023, 2, 13);
    45. //bool ret1 = equal(d1, d2);
    46. //bool ret2 = compare(d1, d2);
    47. bool ret1 = d1 == d2; //d1.operator==(d2);
    48. bool ret2 = d1 > d2; //d1.operator>(d2);
    49. cout << ret1 << endl;
    50. cout << ret2 << endl;
    51. bool ret3 = d1.operator==(d2);
    52. bool ret4 = d1.operator>(d2);
    53. cout << ret1 << endl;
    54. cout << ret2 << endl;
    55. return 0;
    56. }
    注意:
    1. 不能通过连接其他符号来创建新的操作符:比如operator@
    2. 重载操作符必须有一个类类型参数
    3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
    4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
    5.    .*   ::   sizeof   ?:   .   注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

    写在类外面:

    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. //比较相等
    12. bool operator==(const Date& d2)
    13. {
    14. if (_year == d2._year && _month == d2._month && _day == d2._day)
    15. return true;
    16. else
    17. return false;
    18. }
    19. bool operator>(const Date& d2);
    20. private:
    21. int _year;
    22. int _month;
    23. int _day;
    24. };
    25. //比较大于
    26. bool Date::operator>(const Date& d2)
    27. {
    28. if (_year > d2._year)
    29. {
    30. return true;
    31. }
    32. else if (_year == d2._year && _month > _month)
    33. {
    34. return true;
    35. }
    36. else if (_year == d2._year && _month == d2._month && _day > d2._day)
    37. {
    38. return true;
    39. }
    40. return false;
    41. }

    接下来是日期加上一个数。

    1. class Date
    2. {
    3. public:
    4. Date(int year = 2023, int month = 10, int day = 22)
    5. {
    6. _year = year;
    7. _month = month;
    8. _day = day;
    9. cout << "Date的构造" << endl;
    10. }
    11. int GetMonthday(int year, int month)
    12. {
    13. int month_day[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. return month_day[month];
    19. }
    20. Date& operator+=(int day)
    21. {
    22. _day += day;
    23. while (_day > GetMonthday(_year,_month))
    24. {
    25. _day -= GetMonthday(_year, _month);
    26. _month++;
    27. if (_month == 13)
    28. {
    29. _year++;
    30. _month = 1;
    31. }
    32. }
    33. return *this;
    34. }
    35. void Print()
    36. {
    37. cout << _year << "-" << _month << "-" << _day << endl;
    38. }
    39. private:
    40. int _year;
    41. int _month;
    42. int _day;
    43. };
    44. int main()
    45. {
    46. Date d1;
    47. d1 += 50;
    48. d1.Print();
    49. return 0;
    50. }

    但是这样我们把对象d1就做了修改,我们不想修改,只想+然后返回来一个值怎么办?

    将下面这个代码加入类中

    1. Date operator+(int day)
    2. {
    3. //实例化一个对象tmp拷贝*this
    4. Date tmp(*this);
    5. //复用operator+=
    6. tmp += 50;
    7. return tmp;
    8. }
    1. int main()
    2. {
    3. Date d1;
    4. Date d2 = d1 + 50;
    5. d2.Print();
    6. return 0;
    7. }

     

    接下来就是我们的赋值运算符重载了。

    1. Date& Date::operator=(const Date& d)
    2. {
    3. _year = d._year;
    4. _month = d._month;
    5. _day = d._day;
    6. return *this;
    7. }

    前置++和后置++重载

    前置和后置++,最难以区分的其实是他们的名字,尽管返回值不同,但参数相同,不构成重载,于是就有了一个特殊规定,看代码

    C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
    自动传递
    1. //前置++复用operator+=
    2. Date& Date::operator++()
    3. {
    4. *this += 1;
    5. return *this;
    6. }
    7. //后置++复用operator+=
    8. Date Date::operator++(int)
    9. {
    10. Date tmp(*this);
    11. *this += 1;
    12. return tmp;
    13. }

    于是我们用参数类型来区分,一个不传,一个传int类型,编译器就可以区分了。

    一般情况下我们最好还是用前置++,是引用返回,而且没有拷贝,后置++就有一次拷贝,也许一次调用差距不大,但是如果调用10万次就有差别了。

    日期类的实现

    接下来我们将使用各种运算符重载实现各种有意义的日期比较和加减,

    头文件 

    1. #pragma once
    2. #include <iostream>
    3. using namespace std;
    4. class Date
    5. {
    6. public:
    7. Date(int year = 2023, int month = 10, int day = 24);
    8. int GetMonthday(int year, int month);
    9. //operator+复用opreator+=,优于operator+=复用operator+
    10. Date operator+(int day);
    11. Date operator-(int day); //计算日期减天数
    12. int operator-(const Date& d); //计算日期和日期间差几天
    13. Date& operator+=(int day);
    14. Date& operator-=(int day);
    15. Date& operator=(const Date& d);
    16. bool operator==(const Date& d);
    17. bool operator!=(const Date& d);
    18. bool operator>(const Date& d);
    19. bool operator<(const Date& d);
    20. bool operator>=(const Date& d);
    21. bool operator<=(const Date& d);
    22. //前置++复用operator+=
    23. Date& operator++();
    24. //后置++复用operator+=
    25. Date operator++(int);
    26. void Print()
    27. {
    28. cout << _year << "-" << _month << "-" << _day << endl;
    29. }
    30. private:
    31. int _year;
    32. int _month;
    33. int _day;
    34. };

    日期类的实现 

    1. #include "Date.h"
    2. Date::Date(int year, int month, int day)
    3. {
    4. _year = year;
    5. _month = month;
    6. _day = day;
    7. }
    8. int Date::GetMonthday(int year, int month)
    9. {
    10. int month_day[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    11. if (month == 2 && (year % 4 == 0 && year % 100 == 0 || year % 400 == 0))
    12. {
    13. return 29;
    14. }
    15. return month_day[month];
    16. }
    17. Date& Date::operator+=(int day)
    18. {
    19. _day += day;
    20. while (_day > GetMonthday(_year, _month))
    21. {
    22. _day -= GetMonthday(_year, _month);
    23. _month++;
    24. if (_month == 13)
    25. {
    26. _year++;
    27. _month = 1;
    28. }
    29. }
    30. return *this;
    31. }
    32. Date Date::operator+(int day)
    33. {
    34. Date tmp(*this);
    35. tmp += 50;
    36. return tmp;
    37. }
    38. Date Date::operator-(int day)
    39. {
    40. Date tmp(*this);
    41. tmp -= day;
    42. return tmp;
    43. }
    44. Date& Date::operator-=(int day)
    45. {
    46. if (day < 0)
    47. {
    48. return *this += -day;
    49. }
    50. _day -= day;
    51. while (_day <= 0)
    52. {
    53. --_month;
    54. if (_month == 0)
    55. {
    56. --_year;
    57. _month = 12;
    58. }
    59. _day += GetMonthday(_year, _month);
    60. }
    61. }
    62. Date& Date::operator=(const Date& d)
    63. {
    64. _year = d._year;
    65. _month = d._month;
    66. _day = d._day;
    67. return *this;
    68. }
    69. //前置++复用operator+=
    70. Date& Date::operator++()
    71. {
    72. *this += 1;
    73. return *this;
    74. }
    75. //后置++复用operator+=
    76. Date Date::operator++(int)
    77. {
    78. Date tmp(*this);
    79. *this += 1;
    80. return tmp;
    81. }
    82. bool Date::operator>(const Date& d)
    83. {
    84. if (_year > d._year)
    85. {
    86. return true;
    87. }
    88. else if (_year == d._year && _month > _month)
    89. {
    90. return true;
    91. }
    92. else if (_year == d._year && _month == d._month && _day > d._day)
    93. {
    94. return true;
    95. }
    96. return false;
    97. }
    98. bool Date::operator==(const Date& d)
    99. {
    100. if (_year == d._year && _month == d._month && _day == d._day)
    101. return true;
    102. else
    103. return false;
    104. }
    105. bool Date::operator!=(const Date& d)
    106. {
    107. return !(*this == d);
    108. }
    109. bool Date::operator<(const Date& d)
    110. {
    111. return !(*this >= d);
    112. }
    113. bool Date::operator>=(const Date& d)
    114. {
    115. return *this > d || *this == d;
    116. }
    117. bool Date::operator<=(const Date& d)
    118. {
    119. return !(*this <= d);
    120. }
    121. int Date::operator-(const Date& d)
    122. {
    123. int flag = 1;
    124. Date max = *this;
    125. Date min = d;
    126. if (max < min)
    127. {
    128. max = d;
    129. min = *this;
    130. flag = -1;
    131. }
    132. int n = 0;
    133. while (min != max)
    134. {
    135. ++min;
    136. ++n;
    137. }
    138. return n * flag;
    139. }

    测试 

    1. #include "Date.h"
    2. int main()
    3. {
    4. Date d1;
    5. //测试赋值运算符重载
    6. Date d2 = d1;
    7. cout << "测试d2赋值运算符重载" << endl;
    8. d2.Print();
    9. //测试+运算符重载
    10. Date d3(2023, 10, 25);
    11. d3 = d3 + 50;
    12. cout << "测试d3+运算符重载" << endl;
    13. d3.Print();
    14. //测试-运算符重载
    15. Date d4(2023, 10, 25);
    16. d4 = d4 - 50;
    17. cout << "测试d4-运算符重载" << endl;
    18. d4.Print();
    19. //测试==运算符重载
    20. Date d5(2023, 10, 25);
    21. d5 = d5 - 50;
    22. cout << "测试d5==运算符重载" << endl;
    23. cout << (d4 == d5) << endl;
    24. //测试!=运算符重载
    25. Date d6(2023, 10, 25);
    26. cout << "测试d6!=运算符重载" << endl;
    27. cout << (d4 == d6) << endl;
    28. //测试++运算符重载
    29. Date d7(2023, 10, 25);
    30. cout << "测试d7++运算符重载" << endl;
    31. ++d7;
    32. d7.Print();
    33. //测试>=运算符重载
    34. Date d8(2023, 10, 25);
    35. cout << "测试d8>=运算符重载" << endl;
    36. cout << (d7 >= d8) << endl;
    37. cout << endl << "全部通过" << endl;
    38. return 0;
    39. }

    测试结果 

  • 相关阅读:
    程序员基本功的代码
    C++指针和地址偏移在HotSpot VM中的应用
    探索OpenCV中直方图的神奇之处:应用与实现
    基于Springboot图书馆管理系统、Springboot图书借阅系统设计与实现 毕业设计开题报告
    探索arkui(2)--- 布局(列表)--- 2(支持分组/实现响应滚动位置)
    【vite】vite.defineConfig is not a function/npm无法安装第三方包问题
    开发者,你对云计算可能有些误解
    矩阵分析与应用+张贤达
    管理RMAN备份_维护RMAN备份和仓库记录
    Makefile入门(一)
  • 原文地址:https://blog.csdn.net/m0_74824254/article/details/133955394