• 程序设计与算法(三)C++面向对象程序设计 第四周 运算符重载 笔记


    笔记按照中国大学MOOC上北京大学郭炜老师主讲的程序设计与算法(三)C++面向对象程序设计所作,B站上也有资源。原课程链接如下:

    程序设计与算法(三)C++面向对象程序设计

    其他各章节链接如下:

    程序设计与算法(三)C++面向对象程序设计笔记 第一周 从C到C++

    程序设计与算法(三)C++面向对象程序设计笔记 第二周 类和对象基础

    程序设计与算法(三)C++面向对象程序设计笔记 第三周 类和对象提高

    程序设计与算法(三)C++面向对象程序设计笔记 第四周 运算符重载

    程序设计与算法(三)C++面向对象程序设计笔记 第五周 继承

    程序设计与算法(三)C++面向对象程序设计笔记 第六周 多态

    程序设计与算法(三)C++面向对象程序设计笔记 第七周 输入输出和模板

    程序设计与算法(三)C++面向对象程序设计笔记 第八周 标准模板库STL(一)

    程序设计与算法(三)C++面向对象程序设计笔记 第九周 标准模板库STL(二)

    程序设计与算法(三)C++面向对象程序设计笔记 第十周 C++11新特性和C++高级主题

    其他各科笔记汇总

    运算符重载

    运算符重载基本概念

    运算符重载的需求

    C++ 预定义的运算符,比如+、-、/、%、^、&、~、!、|、=、<<、>>、!=、…只能用于基本数据类型的运算:整型、实型、字符型、逻辑型 …,不能用于对象之间的运算

    在数学上,两个复数可以直接进行+、-等运算。但在C++中,直接将+或-用于复数对象是不允许的

    有时会希望,让对象也能通过运算符进行运算,这样代码更简洁,容易理解

    例如:complex_a 和 complex_b 是两个复数对象;求两个复数的和,希望能直接写:complex_a + complex_b

    运算符重载

    运算符重载,就是对已有的运算符( C++ 中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为

    运算符重载的目的是:扩展 C++ 中提供的运算符的适用范围,使之能作用于对象

    同一个运算符,对不同类型的操作数,所发生的行为不同

    • complex_a + complex_b 生成新的复数对象
    • 5 + 4 = 9
    运算符重载的形式

    运算符重载的实质是函数重载

    可以重载为普通函数,也可以重载为成员函数

    把含运算符的表达式转换成对运算符函数的调用

    把运算符的操作数转换成运算符函数的参数

    运算符被多次重载时,根据实参的类型决定调用哪个运算符函数

    返回值类型 operator 运算符 (形参表)
    {
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    运算符重载示例
    class Complex
    {
      public:
        double real,imag;
        Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) { }
        Complex operator-(const Complex & c);
    };
    //重载为成员函数时,参数个数为运算符目数减一 
    
    
    Complex operator+(const Complex & a, const Complex & b)
    {
        return Complex(a.real+b.real,a.imag+b.imag);//返回一个临时对象
    }
    
    Complex Complex::operator-(const Complex & c)
    {
        return Complex(real-c.real, imag-c.imag);   //返回一个临时对象
    }
    //重载为普通函数时,参数个数为运算符目数
    
    
    int main()
    {
           Complex a(4,4),b(1,1),c;
           c = a + b; //等价于c=operator+(a,b);
           cout << c.real << "," << c.imag << endl;
           cout << (a-b).real << "," << (a-b).imag << endl;
           //a-b等价于a.operator-(b)
           return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出:
    5,5
    3,3

    赋值运算符的重载

    赋值运算符 “=” 重载

    有时候希望赋值运算符两边的类型可以不匹配,比如,把一个 int 类型变量赋值给一个 Complex 对象,或把一个 char * 类型的字符串赋值给一个字符串对象,此时就需要重载赋值运算符 “=”

    赋值运算符 “=” 只能重载为成员函数

    class String {
           private:
                   char * str;
           public:
                   String ():str(new char[1]) { str[0] = 0;}
                   const char * c_str() { return str; };
                   String & operator = (const char * s);
                   ~String( ) { delete [] str; }
    };
    
    String & String::operator = (const char * s)
    {    //重载“=”以使得 obj = "hello" 能够成立
        delete [] str;
        str = new char[strlen(s)+1];
        strcpy(str, s);
        return * this;  
        //返回值是String的引用,返回 * this,即对象自身的引用
    }
    
    int main()
    {
        String s;
        s = "Good Luck," ; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
    // String s2 = "hello!";    // 这条语句是初始化语句,不是赋值语句,这里的“=”并不是赋值号,初始化语句要求 s2 用构造函数初始化。不注释掉就会出错
        s = "Shenzhou 8!"; // 等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出:
    Good Luck,
    Shenzhou 8!

    浅拷贝和深拷贝
    class String {
           private:
                  char * str;
           public:
                  String ():str(new char[1]) { str[0] = 0;}
                  const char * c_str() { return str; };
                  String & operator = (const char * s){
                         delete [] str;
                         str = new char[strlen(s)+1];
                         strcpy(str, s);
                         return * this;
                  };
                  ~String( ) { delete [] str; }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    String S1, S2;
    S1 =this;
    S2 = “that”;
    S1 = S2;
    
    • 1
    • 2
    • 3
    • 4

    写 S1 = S2 希望达到的目的是 S1 里面放着的字符串和 S2 里面放着的字符串一样

    如果不重载这个等号,S1 = S2 原本也能够编译通过,因为等号两边的类型完全相同,都是 String 对象。但原生的等号做的是对象复制的工作,会将 S1 里面的每一个字节都变成跟 S2 里面的每一个字节一样,简单地说就是使得 S1 里面的每一个成员变量都变成跟 S2 里面的成员变量一模一样,实际上会导致 S1.str 和 S2.str 指向同一地方

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6s7cB2Sb-1666237342940)(C++ 面向对象程序设计.assets/image-20221011204101958.png)]

    S1.str 原本指向的存储空间会变成内存垃圾

    如果 S1 对象消亡,析构函数将释放 S1.str 指向的空间,则 S2 消亡时还要释放一次,不妥

    另外,如果执行 S1 = “other”;会导致 S2.str 指向的地方被 delete

    因此要在 class String 里添加成员函数:

    String & operator = (const String & s){
                delete [] str;
                str = new char[strlen(s.str)+1];
                strcpy(str, s.str);
                return * this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这么做就够了吗?还有什么需要改进的地方?

     

     

    考虑下面语句:

    String s;
    s = "Hello";
    s = s;
    
    • 1
    • 2
    • 3

    上面等号的写法里一开始会执行 delete 操作,这就很可能会出错。如果等号左右两边的对象是同一个, 应该什么都不做

     

    解决方法:

    String & operator = (const String & s){
           if( this == & s)
                  return * this;
           delete [] str;
           str = new char[strlen(s.str)+1];
           strcpy( str,s.str);
           return * this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    对 operator = 返回值类型的讨论

    为什么是 String & 而不是 void 和 String ?

    对运算符进行重载的时候,好的风格是应该尽量保留运算符原本的特性

     

    考虑:

    a = b = c;
    //执行顺序从右到左,先让 b 等于 c,再让 a 等于 b = c 的返回值
    //b = c 的返回值即 b 的值
    
    ( a = b ) = c; //会修改 a 的值
    //a = b 的返回值实际上是 a 的引用,在 C++ 中赋值运算符的返回值是等号左边变量的引用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    分别等价于:

    a.operator=( b.operator=(c) );
    ( a.operator=(b) ).operator=(c);
    
    • 1
    • 2

     

     

    上面的 String 类是否就没有问题了?

    如果 String 类不写复制构造函数,用编译器生成的缺省的复制构造函数,会面临和 “=” 同样的问题。这个复制构造函数会做复制工作,把被初始化对象里面的 str 变成和参数对象的 str 一模一样,指向同一片存储空间

    用同样的方法处理

    String(String & s)
    {
           str = new char[strlen(s.str)+1];
           strcpy(str,s.str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里就不需要判断当前对象是否和 s 一样,因为当前对象刚刚生成,不可能和现有的对象一样

    运算符重载为友元函数

    一般情况下,将运算符重载为类的成员函数,是较好的选择

    但有时,重载为成员函数不能满足使用要求,重载为普通函数,又不能访问类的私有成员,所以需要将运算符重载为友元

    class Complex
    {
           double real,imag;
           public:
               Complex(double r, double i):real(r),imag(i){ };
               Complex operator+( double r );
    };
    
    Complex Complex::operator+( double r )
    { //能解释 c+5
          return Complex(real + r,imag);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

     

    经过上述重载后:

    Complex c ;
    c = c + 5; //有定义,相当于 c = c.operator +(5);
    
    • 1
    • 2

    但是:

    c = 5 + c; //编译出错
    
    • 1

     

    所以,为了使得上述的表达式能成立,需要将 + 重载为普通函数

    但是普通函数又不能访问私有成员,所以,需要将运算符 + 重载为友元

    class Complex
    {
           double real,imag;
           public:
               Complex(double r, double i):real(r),imag(i){ };
               Complex operator+( double r );
               friend Complex operator + (double r,const Complex & c);
    };
    
    Complex operator+ (double r,const Complex & c)
    { //能解释 5+c
          return Complex(c.real + r, c.imag);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运算符重载实例:可变长整型数组

    要编写可变长整型数组类,使之能如下使用:

    int main() {  
        CArray a; //开始里的数组是空的
        for( int i = 0;i < 5;++i)
                 a.push_back(i); 
        CArray a2,a3;
        a2 = a;                  
        for( int i = 0; i < a.length(); ++i )
                 cout << a2[i] << " " ;     
        a2 = a3; //a2是空的
        for( int i = 0; i < a2.length(); ++i ) //a2.length()返回0
                 cout << a2[i] << " ";
        cout << endl;
        a[3] = 100;
        CArray a4(a);                  
        for( int i = 0; i < a4.length(); ++i )
                 cout << a4[i] << " ";
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    程序输出结果:
    0 1 2 3 4
    0 1 1 100 4

     

    要做哪些事情?

    写一个类一般都要写构造函数、析构函数

    需要 push_back()、length() 成员函数

    要用动态分配的内存来存放数组元素,需要一个指针成员变量,分配的内存在 CArray 类的析构函数里面释放

    参考前面浅拷贝和深拷贝的例子,实现赋值语句 a2 = a 要重载 “=”

    a2 是一个对象名,不是数组名,要让 a2[i] 成立要重载 “[]”,“[]”是双目运算符,一个操作数在“[]”外面,一个操作数在“[]”里面

    CArray a4(a) 用到了 CArray 的复制构造函数,参考前面浅拷贝和深拷贝的例子,不能用缺省的,要自己写复制构造函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ghV0Hzsx-1666237342943)(C++ 面向对象程序设计.assets/image-20221011235620112.png)]

     

     

    class CArray {
       int size; //数组元素的个数
       int *ptr; //指向动态分配的数组
       public:
           CArray(int s = 0); //s代表数组元素的个数
           CArray(CArray & a);
           ~CArray();
           void push_back(int v); //用于在数组尾部添加一个元素v
           CArray & operator=(const CArray & a);
           //用于数组对象间的赋值
           int length() { return size; } //返回数组元素个数
           int & CArray::operator[](int i) //返回值为 int 不行!不支持 a[i] = 4,非引用的函数返回值不可以作为左值使用
           {//用以支持根据下标访问数组元素,
            //如n = a[i] 和 a[i] = 4; 这样的语句
                  return ptr[i];
           }
    };
    
    CArray::CArray(int s):size(s)
    {
          if(s == 0)
                ptr = NULL;
          else
                ptr = new int[s];
    }
    
    CArray::CArray(CArray & a) {
          if(!a.ptr) {
                ptr = NULL;
                size = 0;
                return;
          }
          ptr = new int[a.size];
          memcpy(ptr, a.ptr, sizeof(int) * a.size);
          size = a.size;
    }
    
    CArray::~CArray()
    {
          if(ptr) delete [] ptr;
    }
    
    CArray & CArray::operator=(const CArray & a)
    { //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的对象一样
       if( ptr == a.ptr) //防止a=a这样的赋值导致出错
          return * this;
       if( a.ptr == NULL) { //如果a里面的数组是空的
          if( ptr ) delete [] ptr;
          ptr = NULL;
          size = 0;
          return * this;
       }
       if( size < a.size) { //如果原有空间够大,就不用分配新的空间
             if(ptr)
             delete [] ptr;
             ptr = new int[a.size];
       }
       memcpy(ptr,a.ptr,sizeof(int)*a.size);
       size = a.size; 
       return * this;
    } // CArray & CArray::operator=(const CArray & a)    
    
    void CArray::push_back(int v)
    {  //在数组尾部添加一个元素
       if(ptr) {
             int * tmpPtr = new int[size+1]; //重新分配空间
             memcpy(tmpPtr,ptr,sizeof(int)*size); //拷贝原数组内容
             delete [] ptr;
             ptr = tmpPtr;
       }
       else //数组本来是空的
          ptr = new int[1];
       ptr[size++] = v; //加入新的数组元素
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    这个 push_back() 是比较低效的,比较好的做法是预先分配多一点的空间,C++ STL 里面 vector 的 push_back() 成员函数就采用这种实现方法

    流插入运算符和流提取运算符的重载

    问题:

    out << 5 << "this";
    
    • 1

    为什么能够成立?

    cout是什么?“<<” 为什么能用在 cout 上?

    流插入运算符的重载

    cout 是在 iostream 头文件中定义的 ostream 类的对象

    <<” 能用在 cout 上是因为在 iostream 里对 “<<” 进行了重载

    实际上在 C++ 的语法里,“<<” 和 “>>” 是左移和右移运算符,只不过重载后经常用于输入输出,所以又叫流插入和流提取运算符

     

    考虑,怎么重载才能使得

    cout << 5; cout << "this"
    
    • 1

    都能成立?

     

     

    有可能按以下方式重载成 ostream 类的成员函数:

    void ostream::operator<<(int n)
    {
             ......//输出n的代码
             return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

     

    cout << 5; cout << "this"
    
    • 1

    cout.operator<<(5); cout.operator<<("this");
    
    • 1

     

     

     

    怎么重载才能使得

    cout << 5 << "this";
    
    • 1

    成立?

     

    修改返回值类型为 ostream &

    ostream & ostream::operator<<(int n)
    {
                ......//输出n的代码
                return * this;
    }
    
    ostream & ostream::operator<<(const char * s )
    {
                ......//输出s的代码
                return * this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

     

    cout << 5 <<this;
    
    • 1

    本质上的函数调用的形式是

    cout.operator<<(5).operator<<(this);
    
    • 1

     

     

     

    例子:

    假定下面程序输出为 5hello,该补写些什么?

    class CStudent{
        public: int nAge;
    };
    
    int main(){
        CStudent s ;
        s.nAge = 5;
        cout << s <<"hello";
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    cout 是 ostream 类的对象,ostream 类早就在 iostream 头文件里写好了,不可能再为它添加成员函数,所以在这里重载 “<<” 时只能重载成一个全局函数

     

    ostream & operator<<(ostream & o,const CStudent & s){
          o << s.nAge ;
          return o;
    }
    
    • 1
    • 2
    • 3
    • 4

    这里如果不写 ostream & o 写 ostream o 编译不过

    例题

    假定 c 是 Complex 复数类的对象,现在希望写“cout << c;”,就能以“a+bi”的形式输出c的值,写“cin>>c;”,就能从键盘接受“a+bi”形式的输入,并且使得 c.real = a,c.imag = b

    int main() {
         Complex c;
         int n;
         cin >> c >> n;
         cout << c << "," << n;
         return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W1MbELAm-1666237342945)(C++ 面向对象程序设计.assets/image-20221012213912966.png)]

     

    #include 
    #include 
    #include 
    using namespace std;
    class Complex {
        double real,imag;
        public:
             Complex(double r=0, double i=0):real(r),imag(i){ };
             friend ostream & operator<<(ostream & os,const Complex & c);
             friend istream & operator>>(istream & is,Complex & c);
    };
    
    ostream & operator<<(ostream & os,const Complex & c)
    {
       os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式输出
       return os;
    }
    
    istream & operator>>( istream & is,Complex & c)
    {
       string s;
       is >> s; //将"a+bi"作为字符串读入,“a+bi” 中间不能有空格
       int pos = s.find("+",0);
       string sTmp = s.substr(0,pos); //分离出代表实部的字符串
       c.real = atof(sTmp.c_str()); 
       //atof库函数能将const char*指针指向的内容转换成 float
       sTmp = s.substr(pos+1, s.length()-pos-2);  // 分离出代表虚部的字符串
       c.imag = atof(sTmp.c_str());
       return is;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    “<<” 和 “>>” 运算符只能重载为全局函数,不可能重载为 ostream类 或 istream 类的成员函数,因为这两个类都已经写好了。由于在 Complex 这个类里 real、imag 这两个成员变量都是私有的,全局函数要被声明为 Complex 类的友元才能访问

    类型转换运算符和自增、自减运算符的重载

    重载类型转换运算符

    任何一个类型的名字本身就是类型转换运算符

    #include 
    using namespace std;
    class Complex
    {
       double real,imag;
       public:
             Complex(double r=0,double i=0):real(r),imag(i) { };
             operator double () { return real; }
             //重载强制类型转换运算符 double
    };
    
    int main()
    {
           Complex c(1.2,3.4);
           cout << (double)c << endl; //输出 1.2
           double n = 2 + c;          //等价于 double n=2+c.operator double()
           cout << n;                 //输出 3.2
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    operator double() 是一个函数,返回值类型是 double。类型转换运算符进行重载时不写返回值类型,它的返回值类型就是这个类型本身

    double 是一个单目运算符,(double)c 被解释为 c.operator double()

    如果 c 能够被自动转换成一个 double 类型的东西就可以和2相加,编译器也是这么考虑的

     

    重载类型转换运算符之后,就可以把 Complex 对象转换成一个 double 类型,这个转换可以是显式的,也可以是自动的,在任何可以出现 double 类型变量的地方,如果出现的是一个 Complex 对象,那这个对象就会被自动转换成 double 类型的东西

    自增,自减运算符的重载

    自增运算符 ++ 、自减运算符 – 有前置/后置之分,为了区分所重载的是前置运算符还是后置运算符,C++ 规定:

    • 前置运算符作为一元运算符重载

      • 重载为成员函数:

        T & operator++();
        T & operator--();
        
        • 1
        • 2
      • 重载为全局函数

        T1 & operator++(T2);
        T1 & operator--(T2);
        
        • 1
        • 2
    • 后置运算符作为二元运算符重载,多写一个没用的参数:

      • 重载为成员函数:

        T operator++(int);
        T operator--(int);
        
        • 1
        • 2
      • 重载为全局函数:

        T1 operator++(T2,int);
        T1 operator--(T2,int);
        
        • 1
        • 2

    但是在没有后置运算符重载而有前置重载的情况下,在 vs 中,obj++ 也调用前置重载,而 dev 则令 obj++ 编译出错

     

     

    int main()
    {
           CDemo d(5);
           cout << (d++) << ","; //等价于 d.operator++(0);
           cout << d << ",";
           cout << (++d) << ","; //等价于 d.operator++();
           cout << d << endl;
           cout << (d--) << ","; //等价于 operator--(d,0);
           cout << d << ",";
           cout << (--d) << ","; //等价于 operator--(d);
           cout << d << endl;
           return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出结果:
    5,6,7,7
    7,6,5,5

     

    如何编写 CDemo?

    class CDemo {
      private :
           int n;
      public:
           CDemo(int i=0):n(i) { }
           CDemo & operator++();    //用于前置形式
           CDemo operator++( int ); //用于后置形式
         operator int ( ) { return n; }
         friend CDemo & operator--(CDemo &);
         friend CDemo operator--(CDemo & ,int);
    };
    
    CDemo & CDemo::operator++()
    { //前置 ++
          n ++;
          return * this;
    } // ++s即为:s.operator++();
    
    CDemo CDemo::operator++( int k )
    {//后置 ++
           CDemo tmp(*this); //记录修改前的对象
           n ++;
           return tmp;       //返回修改前的对象
    } // s++即为:s.operator++(0);
    
    CDemo & operator--(CDemo & d)
    {//前置 --
             d.n--;
             return d;
    } //--s即为:operator--(s);
    
    CDemo operator--(CDemo & d,int)
    {//后置 --
            CDemo tmp(d);
            d.n --;
            return tmp;
    } //s--即为:operator--(s, 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在 C++ 里面,++a 的返回值是 a 的引用,a++ 返回一个临时变量,临时变量的值就是 a 被修改之前的值

    后置的++和–生成了一个局部对象,引发复制构造函数调用,返回值也是一个对象,又会引发构造函数调用。前置的++和–里面并没有生成对象,返回的也是引用,运行速度较快

    运算符重载的注意事项

    1. C++ 不允许定义新的运算符
    2. 重载后运算符的含义应该符合日常习惯
      • complex_a + complex_b
      • word_a > word_b
      • date_b = date_a + n
    3. 运算符重载不改变运算符的优先级
    4. 以下运算符不能被重载:“.”、“.*”、“::”、“?:”、sizeof
    5. 重载运算符()、[]、->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数
  • 相关阅读:
    机器学习笔记 - 用于3D点云数据分类的Point Net的训练
    Android app保活(前台服务)
    性能测试-基础01
    布局--QT Designer
    Linux-vim使用
    使用curl执行Http请求
    窗函数的选择
    如何用MyBatis查询数据库
    算法通关村第九关|黄金挑战|有序数组转为二叉搜索树&寻找两个正序数组的中位数
    机器学习:决策树
  • 原文地址:https://blog.csdn.net/zimuzi2019/article/details/127424124