• 运算符重载


    所谓重载,就是重新赋予新的含义。函数重载是对一个已有的函数赋予新的含义,使之实现新功能。

    其实运算符也可以重载,实际上,我们常常在不知不觉之中使用了运算符重载。

    运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。 也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。

    重载运算符的函数一般格式如下:

     函数类型 operator 运算符名称(形参表列) 
    { 
        对运算符的重载处理 
    } 

    例如我们可以重载运算符 + , 如下:

    int operator+(int a, int b)
    {
        return (a – b);
    }

    举个栗子:实现复数加法 (3, 4i)+ (5, -10i)= (8, -6i)

    当我们还不知道重载,我们会这样做: complex.cpp

    #include <iostream>
    
    class Complex
    {
    public:
    	//两类构造函数情况 
        Complex();//第一类构造函数,无初始化  
        Complex(double r, double i);//第二类构造函数,有初始化 
        Complex complex_add(Complex &d);
        void print();
    
    private:
        double real;   
        double imag;
    };
    
    Complex::Complex()
    {
        real = 0;
        imag = 0;
    }
    
    Complex::Complex(double r, double i)
    {
        real = r;
        imag = i;
    }
    
    Complex Complex::complex_add(Complex &d)
    {
        Complex c;
    
        c.real = real + d.real;
        c.imag = imag + d.imag;
    
        return c;
    }
    
    void Complex::print()
    {
        std::cout << "(" << real << ", " << imag << "i)
    ";
    }
    
    int main()
    {
        Complex c1(3, 4), c2(5, -10), c3;
    
        c3 = c1.complex_add(c2);
    
        std::cout << "c1 = ";
        c1.print();
        std::cout << "c2 = ";
        c2.print();
        std::cout << "c1 + c2 = ";
        c3.print();
    
        return 0;
    }
    

      结果:

    c1 = (3, 4i)
    c2 = (5, -10i)
    c1 + c2 = (8, -6i)
    请按任意键继续. . .
    

    当我们朦胧懂得了重载,我们会这样做: complex2.cpp

    #include <iostream>
    
    // 演示对运算符"+"进行重载达到目的!
    
    class Complex
    {
    public:
        Complex();
        Complex(double r, double i);
        Complex operator+(Complex &d);//重载最好用类的函数的类型 
        void print();
    
    private:
        double real;
        double imag;
    };
    
    Complex::Complex()
    {
        real = 0;
        imag = 0;
    }
    
    Complex::Complex(double r, double i)
    {
        real = r;
        imag = i;
    }
    
    Complex Complex::operator+(Complex &d)//重载的实现程 
    {
        Complex c;
    
        c.real = real + d.real;
        c.imag = imag + d.imag;
    
        return c;
    }
    
    void Complex::print()
    {
        std::cout << "(" << real << ", " << imag << "i)
    ";
    }
    
    int main()
    {
        Complex c1(3, 4), c2(5, -10), c3;
    
        c3 = c1 + c2;
    
        std::cout << "c1 = ";
        c1.print();
        std::cout << "c2 = ";
        c2.print();
        std::cout << "c1 + c2 = ";
        c3.print();
    
        return 0;
    }

    我们在声明 Complex 类的时候对运算符进行了重载,使得这个类在用户编程的时候可以完全不考虑函数是如何实现的,直接使用 +, -, *, / 进行负数的运算即可。 其实,我们还可以对运算符重载函数 operator+ 改写得更简练一些:

    Complex Complex::operator+(Complex &c2)
    {
        return Complex(real+c2.real, imag+c2.imag);
    }
    

    一些规则:

    • C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。
    • 除了一下五个不允许重载外,其他运算符允许重载:

    1. .(成员访问运算符)

    2. .*(成员指针访问运算符)

    3. ::(域运算符)

    4. sizeof(尺寸运算符)

    5. ?:(条件运算符)

    • 重载不能改变运算符运算对象(操作数)个数。 
    • 重载不能改变运算符的优先级别。
    • 重载不能改变运算符的结合性。

    • 载运算符的函数不能有默认的参数。

    • 重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应该有一个是类对象或类对象的引用。(也就是说,参数不能全部都是C++的标准类型,这样约定是为了防止用户修改用于标准类型结构的运算符性质)。

    运算符重载函数作为类友元函数:

      不知道刚刚有没有这样的疑问:”+”运算符是双目运算符,为什么刚刚的例子中的重载函数只有一个参数呢?

      解答:实际上,运算符重载函数有两个参数,但由于重载函数是 Complex 类中的成员函数,有一个参数是隐含着的,运算符函数是用 this 指针隐式地访问类对象的成员。

    return Complex(real+c2.real, imag+c2.imag);
    return Complex(this->real+c2.real, this->imag+c2.imag);
    return Complex(c1.real+c2.real, c1.imag+c2.imag);

      那么例子中的 c1 + c2,编译系统把它解释为: c1.operator+(c2)

      即通过对象 c1 调用运算符重载函数,并以表达式中第二个参数(运算符右侧的类对象c2)作为函数实参。

       运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数:放在类外,做 Complex 类的友元函数存在:complex3.cpp

    #include <iostream>
    
    // 演示对运算符"+"进行重载达到目的!
    
    class Complex
    {
    public:
        Complex();
        Complex(double r, double i);
        friend Complex operator+(Complex &c, Complex &d);//放在类外,做 Complex 类的友元函数存在
        void print();
    
    private:
        double real;
        double imag;
    };
    
    Complex::Complex()
    {
        real = 0;
        imag = 0;
    }
    
    Complex::Complex(double r, double i)
    {
        real = r;
        imag = i;
    }
    
    // 注意,这里作为友元函数,不属于Complex,记得别写 :: 咯!
    Complex operator+(Complex &c, Complex &d)
    {
        return Complex(c.real+d.real, c.imag+d.imag);
    }
    
    void Complex::print()
    {
        std::cout << "(" << real << ", " << imag << "i)
    ";
    }
    
    int main()
    {
        Complex c1(3, 4), c2(5, -10), c3;
    
        c3 = c1 + c2;
    
        std::cout << "c1 = ";
        c1.print();
        std::cout << "c2 = ";
        c2.print();
        std::cout << "c1 + c2 = ";
        c3.print();
    
        return 0;
    }

    为什么把运算符函数作为友元函数呢?

      因为运算符函数要访问 Complex 类对象的成员,如果运算符函数不是 Complex 类的友元函数,而是一个普通的函数,它是没有权力访问 Complex 类的私有成员的。

      由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为成员函数。

    练习:

      请听题: 重载运算符 ”+”, ”-”, ”*”, ”/” 实现有理数的加减乘除运算。rational.cpp:

    • 如 1/8 + 7/8 = 1
    • 如 1/8 – 7/8 = -6/8
    • 如 1/8 * 7/8 = 7/64
    • 如 1/8 / 7/8 = 1/7
      #include <iostream>
      #include <string>
      #include <stdlib.h>//包含求绝对值函数abs 
      
      class Rational
      {
      public:
          Rational(int num, int denom);  // num = 分子, denom = 分母
      
          Rational operator+(Rational rhs); // rhs == right hand side
          Rational operator-(Rational rhs);
          Rational operator*(Rational rhs);
          Rational operator/(Rational rhs);
      
          void print();
      
      private:
          void normalize(); // 负责对分数的简化处理
      
          int numerator;    // 分子
          int denominator;  // 分母
      };
      
      Rational::Rational(int num, int denom)
      {
          numerator = num;
          denominator = denom;
      
          normalize();
      }
      
      // normalize() 对分数进行简化操作包括:
      // 1. 只允许分子为负数,如果分母为负数则把负数挪到分子部分,如 1/-2 == -1/2
      // 2. 利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5
      void Rational::normalize()
      {
          // 确保分母为正
          if( denominator < 0 )
          {
              numerator = -numerator;
              denominator = -denominator;
          }
      
          // 欧几里德算法
          int a = abs(numerator);
          int b = abs(denominator);
      
          // 求出最大公约数
          while( b > 0 )
          {
              int t = a % b;
              a = b;
              b = t;
          }
      
          // 分子、分母分别除以最大公约数得到最简化分数
          numerator /= a;
          denominator /= a;
      }
      
      // a   c   a*d   c*b   a*d + c*b
      // - + - = --- + --- = ---------
      // b   d   b*d   b*d =    b*d
      Rational Rational::operator+(Rational rhs)
      {
          int a = numerator;
          int b = denominator;
          int c = rhs.numerator;
          int d = rhs.denominator;
      
          int e = a*b + c*d;
          int f = b*d;
      
          return Rational(e, f);
      }
      
      // a   c   a   -c
      // - - - = - + --
      // b   d   b   d
      Rational Rational::operator-(Rational rhs)
      {
          rhs.numerator = -rhs.numerator;
      
          return operator+(rhs);
      }
      
      // a   c   a*c
      // - * - = ---
      // b   d   b*d
      Rational Rational::operator*(Rational rhs)
      {
          int a = numerator;
          int b = denominator;
          int c = rhs.numerator;
          int d = rhs.denominator;
      
          int e = a*c;
          int f = b*d;
      
          return Rational(e, f);
      }
      
      // a   c   a   d
      // - / - = - * -
      // b   d   b   c
      Rational Rational::operator/(Rational rhs)
      {
          int t = rhs.numerator;
          rhs.numerator = rhs.denominator;
          rhs.denominator = t;
      
          return operator*(rhs);
      }
      
      void Rational::print()  // 1/8
      {
          if( numerator % denominator == 0 )
              std::cout << numerator / denominator;
          else
              std::cout << numerator << "/" << denominator;
      }
      
      int main()
      {
          Rational f1(2, 16);
          Rational f2(7, 8);
      
          // 测试有理数加法运算
          Rational res = f1 + f2;
          f1.print();
          std::cout << " + ";
          f2.print();
          std::cout << " = ";
          res.print();
          std::cout << "
      ";
      
          // 测试有理数减法运算
          res = f1 - f2;
          f1.print();
          std::cout << " - ";
          f2.print();
          std::cout << " = ";
          res.print();
          std::cout << "
      ";
      
          // 测试有理数乘法运算
          res = f1 * f2;
          f1.print();
          std::cout << " * ";
          f2.print();
          std::cout << " = ";
          res.print();
          std::cout << "
      ";
      
          // 测试有理数除法运算
          res = f1 / f2;
          f1.print();
          std::cout << " / ";
          f2.print();
          std::cout << " = ";
          res.print();
          std::cout << "
      ";
      
          return 0;
      }
      

    一些小TIPS

    • 千万不要仅仅因为”我懂得这么做”而去重载一个操作符,应该只在有必要的时候,比如实现一种新的数据类型时,才重载操作符。
    • 重载操作符的目的是为了让代码更容易阅读和理解。令人遗憾的是,这个技巧经常被人们滥用。

    • 注意在重载操作符时,千万不要让它们失去原始的意义,你完全重载 + 然后对它进行减法操作,但这决不是创意的表现,在公司这员工必须被开除 -_-

      

      

  • 相关阅读:
    第一次团队作业
    第二次结对作业
    动态代理与AOP
    笔试题
    java并发面试题(带答案)
    线程问题——同步和死锁
    java线程的方法
    java实现多线程的方法
    使用java闭锁实现并发
    Java多线程——同步问题
  • 原文地址:https://www.cnblogs.com/tianqizhi/p/10420929.html
Copyright © 2020-2023  润新知