【C++】类与对象 第二篇(构造函数,析构函数,拷贝构造,赋值重载)

目录

类的6个默认成员函数

初始化和清理

1.构造函数

2.析构函数

3.共同点

拷贝复制

1.拷贝构造

使用细节

2.赋值重载

运算符重载

== <= < >= > !=

连续赋值

日期类

TestDate.h

TestDate.cpp

test.cpp

const成员

取地址及const取地址操作符重载


C++入门 第一篇(C++关键字, 命名空间,C++输入&输出)-CSDN博客

C++入门 第二篇( 引用、内联函数、auto关键字、指针空值nullptr)-CSDN博客

【C++】类与对象 第一篇(class,this)-CSDN博客

类的6个默认成员函数

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

初始化和清理

1.构造函数

特征: 构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象其特征如下:

  1. 函数名与类名相同。

  2. 无返回值。

  3. 对象实例化时编译器自动调用对应的构造函数。

  4. 构造函数可以重载。

 class Stack
 {
 public:
     Stack()
     {
         _a=nullptr;
         _size=_capacity=0;
     }
 ​
     Stack(int n)
     {
         _a=(int*)malloc(sizeof(int)*n);
         _size=_capacity=0;
     }
 ​
     void Init(int n=4)
     {
         _a=(int*)malloc(sizeof(int)*n);
         if (nullptr==_a)
         {
             perror("malloc is fail");
             return;
         }
         _capacity=n;
         _size=0;
     }
 ​
     void Push(int x)
     {
         //...
         a[_size++]=x;
     }
     //...
     void Dstory()
     {
         //...
     }
 ​
 private:
     int _a;
     int _size;
     int _capacity;
 };
 ​
 int main()
 {
     Stack st;//无参
    //Stack st();//有参
 ​
     st.Push(1);
     st.Push(2);
     st.Push(3);
     st.Push(4);
 ​
     st.Dstory();
 ​
     return 0;
 }

以上述代码为例:

自动调用初始化

注意:调用无参时如:Data d;此处在后面不能➕(),否则编译器会调用有参的。

使用缺省值:

 class Date
 {
 public:
     Date(int year=1,int month=1,int day=1)
     {
         _year=1;
         _month=1;
         _day=1;
     }
 ​
     void print()
     {
         cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
     }
 ​
 private:
     //成员变量
     int _year;
     int _month;
     int _day;
 };
 ​
 int main()
 {
     //Date d1;
     Date d2(2077,2,3);
     d2.print();
 ​
     return 0;
 }

2.析构函数

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

特性 析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~

  2. 无参数无返回值类型。

  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载

  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

例子:

 typedef int DataType;
 class Stack
 {
 public:
     Stack(size_t capacity = 3)
     {
         _array = (DataType *)malloc(sizeof(DataType) * capacity);
         if (NULL == _array)
         {
             perror("malloc申请空间失败!!!");
             return;
         }
         _capacity = capacity;
         _size = 0;
     }
     void Push(DataType data)
     {
         // CheckCapacity();
         _array[_size] = data;
         _size++;
     }
     // 其他方法...
     ~Stack()
     {
         if (_array)
         {
             free(_array);
             _array = NULL;
             _capacity = 0;
             _size = 0;
         }
     }
 ​
 private:
     DataType *_array;
     int _capacity;
     int _size;
 };
 void TestStack()
 {
     Stack s;
     s.Push(1);
     s.Push(2);
 }

3.共同点

如果编译过程不写,那编译器会自动生成一个默认的,但是如果我们实现了任意一个,编译器就不会生成了。

若是自动初始化,那为什么这个地方会生成随机值呢?

C++把类型分成内置类型(基本类型)自定义类型。内置类型就是语言提供的数据类 型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看 下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员 函数。

内置类型(基本类型):int/char/double... /任意类型指针 自定义类型:class/structd定义的

默认生成构造函数:

1.内置类型成员不做处理。

2.自定义类型的成员,会去调用它的默认构造(不用传参数的构造)

 private:
     // 基本类型/内置类型 - 不进初始化
     int _year;
     int _month;
     int _day;

实用场景:

 class Date
 {
 public:
     //内置类型成员不做处理
     void print()
     {
         cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
     }
 ​
 private:
     // 基本类型/内置类型 - 不进初始化
     int _year;
     int _month;
     int _day;
 };
 ​
 class MyQueue
 {
     // 默认生成构造函数,对自定义类型,会调用它的默认构造函数
     void push(int x);
     {
 ​
     }
     //...
   
     Stack _pushST();
     Stack _popST();
 };

析构:

默认生成构造函数:

1.内置类型成员不做处理。

2.自定义类型的成员,会去调用它的析构函数

 class MyQueue
 {
     // 默认生成析构函数,对自定义类型,会调用它的析构函数
     void push(int x);
     {
 ​
     }
     Stack _pushST();
     Stack _popST();
 };
 ​
 int main()
 {
     //Date d1;
     Date d1;
     d1.print();
 ​
     MyQueue q;
 ​
     return 0;
 }

内置类型并不会主动初始化,但可以通过给缺省值进行初始化:

 private:
 //声明位置给缺省值
     int _year=1;
     int _month=1;
     int _day=1;

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。

拷贝复制

1.拷贝构造

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

拷贝构造:内置类型,编译器直接拷贝,自定义类型拷贝需要调用拷贝构造

特征

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式

    为什么自定义类型要用拷贝构造,而内置类型编译器却可以直接拷贝(按字节拷贝)? ​ 因为自定义类型进行拷贝容易出问题:若要拷贝栈,栈的成员变量都是指向相同的空间,若进行拷贝,则会导致两个Stack指向同一个空间,导致原本Stack执行析构函数时被销毁,而复制的那个则指向空,正确的应该是各有各的空间。

  2. 拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

class Date
 {
 public:
     Date(int year=2077, int month=10, int day=12)
     {
         _year = year;
         _month = month;
         _day = day;
     }
     Date(Date d)//这样拷贝会造成无限递归
     {
         _year = d.year;
         _month = d.month;
         _day = d.day;
     }
 ​
 private:
     int _year;
     int _month;
     int _day;
 };

在拷贝构造函数 Date(Date d) 中,参数 d 是按值传递的,这意味着每次调用拷贝构造函数时都会创建一个新的 Date 对象,并将原始对象 d 复制到新的对象中。然而,在拷贝构造函数内部,对于拷贝构造函数的调用又会传递同样的参数 d,导致不断地递归调用拷贝构造函数,从而产生无限递归。

则可以使用 & ,使其不在重新创建空间并使用原Date对象

Date(const Date& d)
 {
     _year = d._year;
     _month = d._month;
     _day = d._day;
 }

这个拷贝构造函数将创建一个新的 Date 对象,并将原始对象的 _year_month_day 成员变量的值分别拷贝到新对象的相应成员变量中。通过使用对象引用作为参数,我们可以避免无限递归调用拷贝构造函数的问题,并且确保在构造新对象时不会复制整个对象

 
class Date
 {
 public:
     Date(int year = 2077, int month = 10, int day = 12)
     {
         _year = year;
         _month = month;
         _day = day;
     }
 ​
     Date(const Date& d)//const是为了防止写反->d._year = _year;加const缩小权限
     {
         cout<<"Date(Date& d);"<<endl;
         _year = d._year;
         _month = d._month;
         _day = d._day;
     }
 ​
 private:
     int _year;
     int _month;
     int _day;
 };
 ​
 int main()
 {
     Date d1(2023, 2, 3);
     //两种拷贝方式
     Date d2(d1); // 使用拷贝构造函数创建对象 d2,并将 d1 的值拷贝到 d2 中
     Date d3=d1;
 ​
     return 0;
 }

RunCode:

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

深拷贝是指在对象拷贝时,复制所有的数据和资源,使得新对象和原对象完全独立,互不影响。与之相对的是浅拷贝,浅拷贝只复制指针或引用,导致新旧对象共享同一份数据,修改一个可能会影响另一个。深拷贝能够保证对象之间的独立性和数据完整性。

分清楚是不是拷贝构造:

 class Date
 {
 public:
     Date(int year = 2077, int month = 10, int day = 12)
     {
         _year = year;
         _month = month;
         _day = day;
     }
   
     //拷贝构造函数   
     Date(const Date& d)
     {
         cout<<"Date(Date& d);"<<endl;
         _year = d._year;
         _month = d._month;
         _day = d._day;
     }
 ​
     //构造函数 不是拷贝构造
     Date(const Date* d)
     {
         cout<<"Date(Date& d);"<<endl;
         _year = d._year;
         _month = d._month;
         _day = d._day;
     }
 private:
     int _year;
     int _month;
     int _day;
 };
 ​
 int main()
 {
     Date d1(2023, 2, 3);
 //Date(const Date* d)
     Date d2(&d1); 
     Date d3=&d1;
 ​
     return 0;
 }

拷贝构造函数典型调用场景:

  • 使用已存在对象创建新对象

  • 函数参数类型为类类型对象

  • 函数返回值类型为类类型对象

使用细节

1使用拷贝构造时最好加上const,以防权限的放大(例如拷贝构造的值:_year = d._year + 1;)

   // 拷贝构造
     Date(const Date &d)
     {
         _year = d.year;
         _month = d.month;
         _year = d.year;
     }

2编译器可自动生成但存在问题

本文所用的日期类可以使用自动生成拷贝,但进行拷贝栈时就会出现问题

当进行拷贝时,用值拷贝,导致两个栈指向同一个空间,造成空间互相覆盖 指向同一块空间的问题:1.插入删除数据会互相影响 2.析构两次,程序崩溃

析构是也跟栈一样是后进先出后定义的先析构,故st2先析构,对st2滞空是,并不影响st1,导致野指针。此时浅拷贝行不通,需要进行深拷贝:

代码实现:

class Stack
 {
 public:
     void Push(const int &data)
     {
         _array[_size] = data;
         _size++;
     }
 ​
     Stack(const Stack &st)
     {
         _array = (int *)malloc(sizeof(int) * st._capacity);
         if (nullptr == _array)
         {
             perror("malloc is fail");
             exit(-1);
         }
         memcpy(_array, st._array, sizeof(int) * st._size);
         _size = st._size;
         _capacity = st._capacity;
     }
 ​
     ~Stack()
     {
         if (_array)
         {
             delete[] _array;
             _array = nullptr;
             _capacity = 0;
             _size = 0;
         }
     }
 ​
 private:
     int *_array;
     int _size;
     int _capacity;
 };
 ​
 int main()
 {
     Stack st1;
     st1.Push(1);
     st1.Push(2);
     st1.Push(3);
 ​
     Stack st2(st1);
 ​
     return 0;
 }

什么情况下需要事项拷贝构造呢? 自己实现了析构释放空间,就需要实现拷贝构造

 class Stack
 {
 public:
     //构造函数  
     Stack(size_t _capacity = 10)
     {
         cout << "Stack(size_t _capacity)" << endl;
         _array = (int *)malloc(sizeof(int) * _capacity);
         if (nullptr == _array)
         {
             perror("malloc is fail");
             exit(-1);
         }
         _size = 0;
         _capacity = _capacity;
     }
 ​
     void Push(const int &data)
     {
         _array[_size] = data;
         _size++;
     }
 ​
     //拷贝构造
     Stack(const Stack &st)
     {
         _array = (int *)malloc(sizeof(int) * st._capacity);
         if (nullptr == _array)
         {
             perror("malloc is fail");
             exit(-1);
         }
         memcpy(_array, st._array, sizeof(int) * st._size);
         _size = st._size;
         _capacity = st._capacity;
     }
 ​
     ~Stack()
     {
         if (_array)
         {
             delete[] _array;
             _array = nullptr;
             _capacity = 0;
             _size = 0;
         }
     }
 ​
 private:
     int *_array;
     int _size;
     int _capacity;
 };
 ​
 class MyQueue
 {
     //默认生成构造 ->生成定义在Stack中的默认构造函数
     //默认生成析构
     //默认生成的拷贝构造
     
 private:
     Stack _pushST;
     Stack _popST;
     int _size = 0;
 };
 ​
 int main()
 {
     Stack st1;
     st1.Push(1);
     st1.Push(2);
     st1.Push(3);
 ​
     Stack st2(st1);
     cout<<"====="<<endl;
     MyQueue q;
 ​
     return 0;
 }

默认生成拷贝构造和赋值重载:

a.内置类型完成 浅/值 拷贝--按byte一个一个拷贝

b.自定义类型,去调用这个成员 拷贝构造/赋值重载

2.赋值重载

运算符重载

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。 函数原型:返回值类型 operator操作符(参数列表)

自定义类型不能直接使用运算操作符 内置类型是语法定义,而自定义类型为人为定义,编译器并不知道该如何进行比较,这便出现了operator用函数完成

运算符重载:自定义类型对象可以使用运算符

函数重载:支持函数名相同,参数不同的函数

 
#include <iostream>
 using namespace std;
 ​
 class Date
 {
 public:
     // 构造函数
     Date(int year = 1, int month = 1, int day = 1)
     {
         _year = year;
         _month = month;
         _day = day;
     }
 ​
     // 拷贝构造
     Date(const Date &d)
     {
         _year = d._year;
         _month = d._month;
         _day = d._day;
     }
 ​
     void Print()
     {
         cout << _year << "/" << _month << "/" << _day << endl;
     }
 ​
     // d1==d2 -> d1.operator==(d2)
     bool operator==(const Date &d)
     {
         return _year == d._year && _month == d._month && _day == d._day;
         //    this->_year==d.year
     }
 ​
 private:
     // 成员变量
     int _year;
     int _month;
     int _day;
 };
 ​
 int main()
 {
     Date d1(2077, 2, 4);
     Date d2(2077, 2, 4);
 ​
     // d1==d2;
     cout << d1.operator==(d2) << endl;
     cout << (d1 == d2) << endl;
 ​
     return 0;
 }

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator

  • 重载操作符必须有一个类类型参数

  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义

  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this现。

  • 注意以下5个运算符不能重载

     .*   ::  sizeof  ?:   .

== <= < >= > !=
// d1==d2 -> d1.operator==(d2)
     bool operator==(const Date &d)
     {
         return _year == d._year && _month == d._month && _day == d._day;
         //    this->_year==d.year
     }
 ​
     // bool operator<(const Date &d)
     // {
     //     if (_year < d._year)
     //     {
     //         return true;
     //     }
     //     else if (_year == d._year && _month < d._month)
     //     {
     //         return true;
     //     }
     //     else if (_year == d._year && _month == d._month && _day < d._day)
     //     {
     //         return true;
     //     }
     //     else
     //         return false;
     // }
 ​
     // d1<d2
     bool operator<(const Date &d)
     {
         return _year < d._year && (_year == d._year && 
        _month < d._month) && (_year == d._year && 
        _month == d._month && _day < d._day);
     }

任何一个类都适用于,当写了一个>=或<=那其他的就可以进行复用

     // d1<=d2
     bool operator<=(const Date &d)
     {
         return *this < d || *this == d;//*this就是d1
     }

这个地方对operator<(const Date &d)进行了复用。operator<=(const Date &d)中的表达式*this < d调用了operator<(const Date &d)来判断两个日期对象的大小关系。同时,operator<=(const Date &d)还利用operator==(const Date &d)来判断两个日期对象是否相等。通过这样的复用方式,可以简化代码并提高代码的可读性

     // d1>d2
     bool operator>(const Date &d)
     {
         return !(*this<= d);
     }
 ​
     //d1>=d2
     bool operator>=(const Date& d)
     {
         return !(*this<d);
     }
 ​
     //d1!=d2
     bool operator!=(const Date& d)
     {
         return !(*this ==d);
     }

当然赋值运算符不使用&并不会无限循环

    // d1<=d2
     bool operator<=(const Date d)
     {
         return *this < d || *this == d;
     }

当然可以不用,但是最好还是加上

连续赋值

d3 = d1 = d2

     // 因为Date operator=(const Date &d) 出了该函数的作用域*this/d1 还在此时返回的*this是临时拷贝,需要进行拷贝重新开空间造成浪费,不如直接进行&
     Date operator=(const Date &d)//&d 引用
     {
         if (this != &d)//防止d1=d1进行赋值 此处的&为去地址 ,若this与d的地址一样则无需赋值
         {
             _year = d._year;
             _month = d._month;
             _day = d._day;
         }
 ​
         return *this; //*this就是d1
     }                 // 返回值为了支持连续赋值,保持运算符特性  d3=d1=d2;

当然要注意,赋值重载是针对已经创造的对象

 Date d5=d1;//拷贝构造

像这样就是拷贝构造

同样作为默认成员函数,可以不用手动写

源码:

 #include <iostream>
 using namespace std;
 ​
 class Date
 {
 public:
     // 构造函数
     Date(int year = 1, int month = 1, int day = 1)
     {
         _year = year;
         _month = month;
         _day = day;
     }
 ​
     // 拷贝构造
     Date(const Date &d)
     {
         _year = d._year;
         _month = d._month;
         _day = d._day;
     }
 ​
     void Print()
     {
         cout << _year << "/" << _month << "/" << _day << endl;
     }
   
     // d1==d2 -> d1.operator==(d2)
     bool operator==(const Date &d)
     {
         return _year == d._year && _month == d._month && _day == d._day;
         //    this->_year==d.year
     }
 ​
     // d1<d2
     bool operator<(const Date &d)
     {
         return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
     }
 ​
     // d1<=d2
     bool operator<=(const Date &d)
     {
         return *this < d || *this == d; //*this就是d1
     }
 ​
     // d1>d2
     bool operator>(const Date &d)
     {
         return !(*this <= d);
     }
 ​
     // d1>=d2
     bool operator>=(const Date &d)
     {
         return !(*this < d);
     }
 ​
     // d1!=d2
     bool operator!=(const Date &d)
     {
         return !(*this == d);
     }
 ​
     // // 因为Date operator=(const Date &d) 出了该函数的作用域*this/d1 还在此时返回的*this是临时拷贝,需要进行拷贝重新开空间造成浪费,不如直接进行&
     // Date operator=(const Date &d)//&d 引用
     // {
     //     if (this != &d)//防止d1=d1进行赋值 此处的&为去地址 ,若this与d的地址一样则无需赋值
     //     {
     //         _year = d._year;
     //         _month = d._month;
     //         _day = d._day;
     //     }
 ​
     //     return *this; //*this就是d1
     // }                 // 返回值为了支持连续赋值,保持运算符特性  d3=d1=d2;
 ​
 private:
     // 成员变量
     int _year;
     int _month;
     int _day;
 };
 ​
 int main()
 {
     Date d1(2077, 2, 4);
     Date d2(2222, 3, 2);
     Date d3(2078, 2, 4);
 ​
     cout << (d1 < d2) << endl;
     // d3 = d1 = d2;
 ​
     d1 = d2;
     d1.Print();
 ​
     return 0;
 }

日期类

TestDate.h

#pragma once
 #include <iostream>
 #include <assert.h>
 using namespace std;
 ​
 class Date
 {
     // 友元函数
     // friend void operator<<(ostream &out, const Date &d);
     friend ostream &operator<<(ostream &out, const Date &d);
 ​
     friend istream &operator>>(istream &in, Date &d); // 不能加const,为输出型参数
 ​
 public:
     int GetMonthDay(int year, int month);
     // 构造函数
     Date(int year = 1, int month = 1, int day = 1);
     void Print();
 ​
     // d1==d2 -> d1.operator==(d2)
     bool operator==(const Date &d);
 ​
     // d1<d2
     bool operator<(const Date &d);
 ​
     // d1<=d2
     bool operator<=(const Date &d);
 ​
     // d1>d2
     bool operator>(const Date &d);
 ​
     // d1>=d2
     bool operator>=(const Date &d);
 ​
     // d1!=d2
     bool operator!=(const Date &d);
 ​
     // d1+=100
     Date &operator+=(int day);
 ​
     // d1+100
     Date operator+(int day);
 ​
     // d1-=100;
     Date &operator-=(int day);
 ​
     // d1-100
     Date operator-(int day);
 ​
     //++d1
     Date operator++();
 ​
     // d2++
     Date operator++(int); //()内参数(int)无实际意义
 ​
     //--d1
     Date operator--();
 ​
     // d2--
     Date operator--(int); //()内参数(int)无实际意义
 ​
     // d1-d2
     int operator-(const Date &d);
 ​
 private:
     // 成员变量
     int _year;
     int _month;
     int _day;
 };
 ​
 // void operator<<(ostream &out, const Date &d);
 // 为了支持连续调用➕返回值
 ostream &operator<<(ostream &out, const Date &d); // 加const是为了减少拷贝
 ​
 // 流提取
 istream &operator>>(istream &in, Date &d); // 不能加const,为输出型参数

直接用内联函数进行定义,无需在对其他.cpp进行链接: 类里面短小函数,适合做内联的函数,直接是在类里面定义

 inline ostream &operator<<(ostream &out, const Date &d)
 {
     out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
     return out;
 }
 inline istream &operator>>(istream &in, Date &d)
 {
     in >> d._year >> d._month >> d._day;
     return in;
 }

TestDate.cpp

#include "TestDate.h"
 ​
 // 判断该月份天数
 int Date::GetMonthDay(int year, int month)
 {
     assert(month > 0 && month < 13);
 ​
     int monthArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
     if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0))
     {
         return 29;
     }
     else
         return monthArray[month];
 }
 ​
 Date::Date(int year, int month, int day)
 {
     // 判断日期是否合法
     if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month)))
     {
         _year = year;
         _month = month;
         _day = day;
     }
     else
     {
         cout << "日期非法" << endl;
     }
 }
 ​
 void Date::Print()
 {
     cout << _year << "/" << _month << "/" << _day << endl;
 }
 ​
 // d1==d2 -> d1.operator==(d2)
 bool Date::operator==(const Date &d)
 {
     return _year == d._year && _month == d._month && _day == d._day;
     //    this->_year==d.year
 }
 ​
 // d1<d2
 bool Date::operator<(const Date &d)
 {
     return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
 }
 ​
 // d1<=d2
 bool Date::operator<=(const Date &d)
 {
     return *this < d || *this == d; //*this就是d1
 }
 ​
 // d1>d2
 bool Date::operator>(const Date &d)
 {
     return !(*this <= d);
 }
 ​
 // d1>=d2
 bool Date::operator>=(const Date &d)
 {
     return !(*this < d);
 }
 ​
 // d1!=d2
 bool Date::operator!=(const Date &d)
 {
     return !(*this == d);
 }
 ​
 // d1+=d2
 Date &Date::operator+=(int day) // 出作用域*this还在,故用引用返回Date*
 {
     //+= --50
     if (day < 0)
     {
         *this -= -day;
         return *this;
     }
     _day += day;
     while (_day > GetMonthDay(_year, _month))
     {
         _day -= GetMonthDay(_year, _month);
         ++_month;
         if (_month == 13)
         {
             ++_year;
             _month = 1;
         }
     }
     return *this;
 }
 ​
 // d1+d2
 Date Date::operator+(int day)
 {
     Date tmp(*this); // 拷贝构造函数的定义,拷贝临时对象
 ​
     // tmp._day += day;
     // while (tmp._day > GetMonthDay(tmp._year,tmp. _month))
     // {
     //     tmp._day -= GetMonthDay(tmp._year, tmp._month);
     //     ++tmp._month;
     //     if (tmp._month == 13)
     //     {
     //         ++tmp._year;
     //         tmp._month = 1;
     //     }
     // }
     // return tmp;
 ​
     tmp += day; // 对Date& operator+=(int day)进行复用
     return tmp;
 }
 ​
 /*
 对于第一种实现方式:*this += day,它使用了+=运算符重载,避免了拷贝构造函数的调用,只对当前对象进行了原地修改,因此效率更高。
 而对于第二种实现方式:*this = *this + day,它在+运算符重载中使用了拷贝构造函数创建了临时对象tmp,之后又将临时对象赋值给*this,进行了两次拷贝操作,因此效率较低。
 所以,第一种实现方式更好。
 */
 // Date Date::operator+(int day)
 // {
 //     Date tmp(*this); // 拷贝构造函数的定义,拷贝临时对象
 ​
 //     tmp._day += day;
 //     while (tmp._day > GetMonthDay(tmp._year,tmp. _month))
 //     {
 //         tmp._day -= GetMonthDay(tmp._year, tmp._month);
 //         ++tmp._month;
 //         if (tmp._month == 13)
 //         {
 //             ++tmp._year;
 //             tmp._month = 1;
 //         }
 //     }
 //     return tmp;
 // }
 ​
 // Date& Date::operator+=(int day)
 // {
 //     *this=*this+day;
 //     return *this;
 // }
 ​
 //++d1 返回的是++后的值
 Date Date::operator++()
 {
     *this += 1;
     return *this;
 }
 // d2++ 返回的是++前的值 ,由此可得:前置优于后置
 Date Date::operator++(int)
 {
     Date tmp(*this);
     *this += 1;
     return tmp; // 出作用域销毁无需销毁
 }
 // d1-=100;
 Date &Date::operator-=(int day)
 {
     if (day < 0)
     {
         *this -= -day;
         return *this;
     }
 ​
     _day -= day;
     while (_day <= 0)
     {
         --_month;
         if (_month == 0)
         {
             --_year;
             _month = 12;
         }
         _day += GetMonthDay(_year, _month);
     }
     return *this;
 }
 ​
 // d1-100
 Date Date::operator-(int day)
 {
     Date tmp(*this);
     *this -= day;
     return tmp;
 }
 ​
 //--d1
 Date Date::operator--()
 {
     *this -= 1;
     return *this;
 }
 ​
 // d2--
 Date Date::operator--(int)
 {
     Date tmp(*this);
     *this -= 1;
     return tmp;
 }
 ​
 // d1-d2
 int Date::operator-(const Date &d)
 {
     Date max = *this; // d1
     Date min = d;     // d2
     int flag = 1;
 ​
     if (*this < d)
     {
         max = d;
         min = *this;
         flag = -1;
     }
 ​
     int n = 0;
     while (min != max)
     {
         ++min;
         ++n;
     }
     return n * flag;
 }
 ​
 // void operator<<(ostream &out,const Date& d)
 // {
 //     out<<d._year<<"年"<<d._month<<"月"<<d._day<<"日"<<endl;
 // }
 ​
 //➕返回值
 // ostream& operator<<(ostream &out,const Date& d)
 // {
 //     out<<d._year<<"年"<<d._month<<"月"<<d._day<<"日"<<endl;
 //     return out;
 // }
 // istream& operator>>(istream& in,Date& d)
 // {
 //     in>>d._year>>d._month>>d._day;
 //     return in;
 // }
 //在TestDate中改为了内联函数

test.cpp

 #include "TestDate.h"
 ​
 void TestDate1()
 {
     Date d1(2023, 2, 4);
     d1.Print();
     cout << "===" << endl;
 ​
     Date d2 = d1 + 150;
     d1.Print();
     d2.Print();
     cout << "===" << endl;
 ​
     Date d3 = d1;
     d3 += 100;
     // d1+=d3+=100;这样会报错,d1+=d3为 日期类+日期类,
     d1.Print();
     d3.Print();
     cout << "===" << endl;
 }
 ​
 void TestDate2()
 {
     Date d1(2023, 2, 4);
     d1.Print();
     ++d1; // d1.operator++();
     d1.Print();
     d1++; // d1.operator++(1);//括号内传整形(不一定是1)只是为了占位,跟前置区分
     d1.Print();
     cout << "===" << endl;
 }
 ​
 void TestDate3()
 {
     Date d1(2023, 2, 4);
     d1.Print();
     d1 -= 100;
     d1.Print();
     cout << "=====" << endl;
     Date d2(2023, 2, 4);
     d2.Print();
     d2 += -100;
     d2.Print();
     cout << "=====" << endl;
     Date d3(2023, 2, 4);
     d3.Print();
     d3 -= -100;
     d3.Print();
     cout << "===" << endl;
 ​
     Date d4(2023, 2, 4);
     d4.Print();
     d4--;
     d4.Print();
     cout << "===" << endl;
     --d4;
     d4.Print();
 }
 ​
 void TestDate4()
 {
     Date d1(2023, 11, 1);
     d1.Print();
     Date d2(2022, 11, 3);
     d2.Print();
     cout << d2 - d1 << endl;
     cout << (d1 - d2) << endl;
 }
 ​
 void TestDate5()
 {
     // 流插入
     int i = 1;
     double d=1.11;
 ​
     cout << i; // cout.operator(i) //int
     cout << d; // cout.operator(d) //double
     cout<<endl;
     cout<<"===="<<endl;
 ​
     //实现流插入
     Date d1(2023, 11, 2);
     // operator<<(cout);
     // d1<<cout;
     operator<<(cout,d1);
     cout<<d1;
     cout<<endl;
     Date d2(2023, 11, 2);
     //此处为连续调用 每次 <<执行完应该return值以便继续调用
     cout<<d1<<d2<<endl;
 ​
 }
 ​
 void TestDate6()
 {
     //流提取
     Date d1;
     cin>>d1;
     cout<<d1;
 ​
 }
 ​
 int main()
 {
     TestDate6();
 ​
     return 0;
 }

const成员

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

流插入和提取 都不是成员函数,不能用const修饰

 int GetMonthDay(int year, int month) const;
 ​
     // 构造函数
     Date(int year = 1, int month = 1, int day = 1);
 ​
     void Print() const;
 ​
     // d1==d2 -> d1.operator==(d2)
     bool operator==(const Date &d) const;
 ​
     // d1<d2
     bool operator<(const Date &d) const;
 ​
     // d1<=d2
     bool operator<=(const Date &d) const;
 ​
     // d1>d2
     bool operator>(const Date &d) const;
 ​
     // d1>=d2
     bool operator>=(const Date &d) const;
 ​
     // d1!=d2
     bool operator!=(const Date &d) const;
 ​
     // d1+=100
     Date &operator+=(int day);
 ​
     // d1+100
     Date operator+(int day) const;
 ​
     // d1-=100;
     Date &operator-=(int day);
 ​
     // d1-100
     Date operator-(int day) const;
 ​
     //++d1
     Date operator++();
 ​
     // d2++
     Date operator++(int); //()内参数(int)无实际意义
 ​
     //--d1
     Date operator--();
 ​
     // d2--
     Date operator--(int); //()内参数(int)无实际意义
 ​
     // d1-d2
     int operator-(const Date &d) const;


 
int Date::GetMonthDay(int year, int month)const
 {
     assert(month > 0 && month < 13);
 ​
     int monthArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
     if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0))
     {
         return 29;
     }
     else
         return monthArray[month];
 }
 ​
 Date::Date(int year, int month, int day)
 {
     // 判断日期是否合法
     if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month)))
     {
         _year = year;
         _month = month;
         _day = day;
     }
     else
     {
         cout << "日期非法" << endl;
     }
 }
 ​
 void Date::Print()const
 {
     cout << _year << "/" << _month << "/" << _day << endl;
 }
 ​
 // d1==d2 -> d1.operator==(d2)
 bool Date::operator==(const Date &d)const
 {
     return _year == d._year && _month == d._month && _day == d._day;
     //    this->_year==d.year
 }
 ​
 // d1<d2
 bool Date::operator<(const Date &d)const
 {
     return _year < d._year && (_year == d._year && _month < d._month) && (_year == d._year && _month == d._month && _day < d._day);
 }
 ​
 // d1<=d2
 bool Date::operator<=(const Date &d)const
 {
     return *this < d || *this == d; //*this就是d1
 }
 ​
 // d1>d2
 bool Date::operator>(const Date &d)const
 {
     return !(*this <= d);
 }
 ​
 // d1>=d2
 bool Date::operator>=(const Date &d)const
 {
     return !(*this < d);
 }
 ​
 // d1!=d2
 bool Date::operator!=(const Date &d)const
 {
     return !(*this == d);
 }
 ​
 // d1+=d2
 Date &Date::operator+=(int day) // 出作用域*this还在,故用引用返回Date*
 {
     //+= --50
     if (day < 0)
     {
         *this -= -day;
         return *this;
     }
     _day += day;
     while (_day > GetMonthDay(_year, _month))
     {
         _day -= GetMonthDay(_year, _month);
         ++_month;
         if (_month == 13)
         {
             ++_year;
             _month = 1;
         }
     }
     return *this;
 }
 ​
 // d1+d2
 Date Date::operator+(int day)const
 {
     Date tmp(*this); // 拷贝构造函数的定义,拷贝临时对象
     tmp += day; // 对Date& operator+=(int day)进行复用
     return tmp;
 }
 ​
 //++d1 返回的是++后的值
 Date Date::operator++()
 {
     *this += 1;
     return *this;
 }
 // d2++ 返回的是++前的值 ,由此可得:前置优于后置
 Date Date::operator++(int)
 {
     Date tmp(*this);
     *this += 1;
     return tmp; // 出作用域销毁无需销毁
 }
 // d1-=100;
 Date &Date::operator-=(int day)
 {
     if (day < 0)
     {
         *this -= -day;
         return *this; 
     }
 ​
     _day -= day;
     while (_day <= 0)
     {
         --_month;
         if (_month == 0)
         {
             --_year;
             _month = 12;
         }
         _day += GetMonthDay(_year, _month);
     }
     return *this;
 }
 ​
 // d1-100
 Date Date::operator-(int day) //const
 {
     Date tmp(*this);
     *this -= day;
     return tmp;
 }
 ​
 //--d1
 Date Date::operator--()
 {
     *this -= 1;
     return *this;
 }
 ​
 // d2--
 Date Date::operator--(int)
 {
     Date tmp(*this);
     *this -= 1;
     return tmp;
 }
 ​
 // d1-d2
 int Date::operator-(const Date &d)const
 {
     Date max = *this; // d1
     Date min = d;     // d2
     int flag = 1;
 ​
     if (*this < d)
     {
         max = d;
         min = *this;
         flag = -1;
     }
 ​
     int n = 0;
     while (min != max)
     {
         ++min;
         ++n;
     }
     return n * flag;
 }

取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

 class Date
 {
 public :
     Date* operator&()
     {
       return this ;
      }
     const Date* operator&()const
     {
         return this ;
     }
 private :
   int _year ; // 年 
   int _month ; // 月 int _day ; // 日
 };

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需 要重载,比如想让别人获取到指定的内容!

#include <iostream>
#include <assert.h>
using namespace std;

class Array
{
public:
    int &operator[](int i)
    {
        assert(i < 10);

        return _a[i];
    }

    const int &operator[](int i) const
    {
        assert(i < 10);

        return _a[i];
    }

private:
    int _a[10];
    int _size;
};

void Func(const Array &aa)
{
    for (int i = 0; i < 10; i++)
    {
        cout << aa[i] << " ";
    }
}

int main()
{
    Array a;
    for (int i = 0; i < 10; i++)
    {
        a[i] = i;
    }

    for (int i = 0; i < 10; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;

    Func(a);
    cout << endl;

    return 0;
}

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值