【C++入门】C++ 运算符的重载

C++运算符的重载

在这里插入图片描述

一、运算符重载基本概念

  • 1.运算符重载的需求

    • C++预定义的运算符,只能用于基本数据类型的运算:整型、实型、字符型、逻辑型…
      +、-、*、/、%、^、&、~、!、|、=、<<、>>、!=…
    • 在数学上,两个复数可以直接进行+、-等运算。但在C++中,直接将+或-用于复数对象是不允许的。
    • 有时会希望,让对象也能通过运算符进行运算。这样代码更简洁,容易理解
    • 例如:
      complex_a和complex_b是两个复数对象;求两个复数的和, 希望能直接写:complex_a + complex_b
  • 2.运算符重载概念

    • 运算符重载,就是对已有的运算符(C++中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。
    • 运算符重载的目的是:扩展C++中提供的运算符的适用范围,使之能作用于对象。
    • 同一个运算符,对不同类型的操作数,所发生的行为不同。
  • 3.运算符重载的形式

    • 运算符重载的实质是函数重载
    • 可以重载为普通函数,也可以重载为成员函数
    • 把含运算符的表达式转换成对运算符函数的调用。
    • 把运算符的操作数转换成运算符函数的参数。
    • 运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
    • 运算符重载的格式:
      返回值类型 operator 运算符(形参表)
      {
      	……
      }
      
  • 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;
    }
    

    输出:
    5,5
    3,3

    c = a + b; 等价于c=operator+(a,b);
    a-b 等价于a.operator-(b)

二、赋值运算符的重载

  • 1.赋值运算符 ‘=’重载

  • 有时候希望赋值运算符两边的类型可以不匹配,比如,把一个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::~String() { delete[] str; }
    };
    String &String::operator=(const char *s)
    { //重载“=”以使得 obj = “hello”能够成立
        delete[] str;
        str = new char[strlen(s) + 1];
        strcpy(str, s);
        return *this;
    }
    int main()
    {
        String s;
        s = "Good Luck,"; //等价于 s.operator=("Good Luck,");
        cout << s.c_str() << endl;
        // String s2 = "hello!"; //这条语句要是不注释掉就会出错
        s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!");
        cout << s.c_str() << endl;
        return 0;
    }
    

    输出:
    Good Luck,
    Shenzhou 8!

  • 2.浅拷贝和深拷贝

    • 浅拷贝:通过赋值运算符“=”,没有经过重载,“=”的作用是逐个字节的复制。

      	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; }
      	};
      
    • 举例:

      String S1, S2;
      S1 = “this”;
      S2 = “that”;
      S1 = S2;
      在这里插入图片描述

    • 如不定义自己的赋值运算符,那么S1=S2实际上导致 S1.str和 S2.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;
      }
      
    • 出现问题:下列语句

      String s;
      s = “Hello”;
      s = s;

    • 改进办法:

      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;
      }
      
  • 3.对 operator = 返回值类型的讨论
    对比voidStringString &

    考虑:

     a = b = c;
     (a=b)=c; //会修改a的值
    

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

    a = b = c; 等价于a.operator=(b.operator=(c));
    (a=b)=c; 等价于 (a.operator=(b)).operator=(c);

    • 为 String类编写复制构造函数的时候,会面临和 = 同样的问题,用同样的方法处理:
      String(String &s)
      {
          str = new char[strlen(s.str) + 1];
          strcpy(str, s.str);
      }
      

三、运算符重载为友元函数

  • 1.运算符重载为友元函数需求
    一般情况下,将运算符重载为类的成员函数,是较好的选择。 但有时,重载为成员函数不能满足使用要求,重载为普通函数,又不能访问类的私有成员,所以需要将运算符重载为友元。

  • 2.运算符重载为友元函数示例

    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);
    }
    

    经过上述重载后:
    Complex c ;
    c = c + 5; //有定义,相当于 c = c.operator +(5);
    但是:
    c = 5 + c; //编译出错

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

    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.流插入运算符的重载

    • cout 是在 iostream中定义的ostream 类的对象。
    • <<” 能用在cout 上是因为,在iostream里对 “<<” 进行了重载。
    • 重载形式:
      ostream &ostream::operator<<(int n)
      {
          …… //输出n的代码
              return *this;
      }
      ostream &ostream::operator<<(const char *s)
      {
          …… //输出s的代码
              return *this;
      }
      
  • 2.例题

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

    #include <iostream>
    #include <string>
    #include <cstdlib>
    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;
    }
    

    主函数:

    int main()
    {
        Complex c;
        int n;
        cin >> c >> n;
        cout << c << "," << n;
        return 0;
    }
    

    运行结果:

    13.2+133i 87↙
    13.2+133i, 87

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

  • 1.重载类型转换运算符

    #include <iostream>
    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
    }
    
  • 2.自增,自减运算符的重载
    自增运算符++、自减运算符–有前置、后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:

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

      /*重载为成员函数:*/
      T &operator++();
      T &operator--();
      /*重载为全局函数:*/
      T1 &operator++(T2);
      T1 &operator(T2);
      
    • 后置运算符作为二元运算符重载,多写一个没用的参数:

      /*重载为成员函数:*/
      T operator++(int);
      T operator--(int);
      /*重载为全局函数:*/
      T1 operator++(T2, int);
      T1 operator(T2, int);
      

六、运算符重载的注意事项

  • 1.C++不允许定义新的运算符 ;
  • 2.重载后运算符的含义应该符合日常习惯;
    • complex_a + complex_b
    • word_a > word_b
    • date_b = date_a + n
  • 3.运算符重载不改变运算符的优先级;
  • 4.以下运算符不能被重载:“.”、“.*”、“::”、“?:”、sizeof;
  • 5.重载运算符()、[]、->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数。

【知识索引】【C++入门】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BkbK-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值