• 运算符重载


    1. 基本概念

       所谓重载,就是重新赋予新的含义。不仅函数可以重载,运算符也可以重载。

           a. 运算符重载的本质是一个函数。

           b. 实现运算符重载是通过operator关键字实现的,运算符重载可以通过类的成员函数和类的友元函数来实现

              区别在于成员函数具有this指针,通过对象调用来传递参数,友元函数没有this指针。

           c. 重载为类的成员函数时,类的this指针会被绑定到运算符的左侧运算对象,因此成员运算符重载函数的显示参数比运算符对象总数少一个,

              即也比全局的运算符重载函数少一个参数。也就是说要想重载为成员函数,运算符的左操作数必须是该类类型的对象。

           d. 重载为全局的函数,意味着失去了运算符左侧类型的限制。对于二元运算符,全局函数第一个参数即为运算符左侧对象,第二个参数为右侧对象。

           e. 不论重载为友元函数还是成员函数,运算符的调用方法相同,但传递参数的方式不同,实现的代码不同,应用场合也不同。

       接下来我们来推演一下运算符重载函数进化为成员函数的过程:

       1)相加函数作为普通函数

    class Complex
    {
    public:
        int a, b;
    
    public:
        Complex(int a = 0, int b = 0)
        {
            this->a = a;
            this->b = b;
        }
    };
    
    Complex Add(Complex &c1, Complex &c2)
    {
        Complex tmp(c1.a + c2.a, c1.b + c2.b);
        return tmp;
    }
    
    int main()
    {
        Complex c1(1, 2), c2(3, 4);
        // 1 普通函数
        Complex c3 = Add(c1, c2);
        return 0;
    }
    

       2)相加函数作为友元函数

    class Complex
    {
    public:
        int a, b;
        friend Complex operator+(Complex &c1, Complex &c2);
    
    public:
        Complex(int a = 0, int b = 0)
        {
            this->a = a;
            this->b = b;
        }
    };
    
    Complex operator+(Complex &c1, Complex &c2)
    {
        Complex tmp(c1.a + c2.a, c1.b + c2.b);
        return tmp;
    }
    
    int main()
    {
        Complex c1(1, 2), c2(3, 4);
        // 2 友元函数,这里也可以写成:Complex c3 = operator+(c1, c2)
        Complex c3 = c1 + c2;
        return 0;
    }
    

       3)相加函数作为类成员函数

    class Complex
    {
    public:
        int a, b;
        Complex operator+(Complex &c2)
        {
            Complex tmp;
            tmp.a = this->a + c2.a ;
            tmp.b = this->b + c2.b ;
            return tmp;
        }
    
    public:
        Complex(int a = 0, int b = 0)
        {
            this->a = a;
            this->b = b;
        }
    };
    
    int main()
    {
        Complex c1(1, 2), c2(3, 4);
        // 3 成员函数,这里也可以写成:Complex c3 = c1.operator+(c2)
        Complex c3 = c1 + c2;
        return 0;
    }
    

    2. 什么时候运算符重载函数需要返回一个引用?

       其实本质就是要判断:你需要返回的是对象本身还是返回一个新的对象。

    3. 重载>>和<<运算符

       1)istream 和 ostream 是 C++ 的预定义流类

       2)cin 是 istream 的对象,cout 是 ostream 的对象

       3)运算符 << 由ostream 重载为插入操作,用于输出基本类型数据

       4)运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据

       5)用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型

       我们首先来观察一下它的调用形式:

    // cout.operator<<(c1).operator<<("abcd");
    cout << c1 << "abcc";

       很明显,如果要重载为成员函数,必须重载为运算符左边对象类型的成员函数,因此得拿到cout这个类的源码。

       故只能使用友元函数重载>>或者<<运算符。结论:当无法修改左操作数的类时,使用友元函数进行重载

       具体代码如下:

    ostream& operator<<(ostream &out, Complex &c1)
    {
        out << c1.a << " + " << c1.b << "i " << endl;
        return out;
    }
    

       为什么要返回引用呢?

       因为对于连续输出的情况来讲,操作的都是同一个对象,比如上面先输出c1,然后再输出abcc,都是在同一个cout对象上操作。

    4. 重载赋值运算符=

       =运算符必须重载为成员函数,理由如下:

       1)一个c++类,如果没有为其定义赋值操作符重载函数,编译器也会隐式的定义,这样倘若再定义全局的赋值运算符重载函数,将会发生二义性。

       2)如果赋值操作符可以作为全局函数重载的话,可能会出现表达错误的语句,int operator=(int a, cls &b);

          这样重载之后,语句2 = a; 表述也是正确的,但却是明显的语法错误。

    Complex& operator=(const Complex& c)
    {
        if (*this != c)
        {
            a = c.a;
            b = c.b;
        }
        return *this;
    }

       返回引用,很明显复值后返回的当然还是对象自身。

    5. 重载[]操作符

       x 是类 X 的一个对象,则表达式 x[y] 可被解释为 x.operator[](y)

       []是二元运算符,且必须重载为成员函数。如果重载为全局的函数,很容易写出如下错误的代码:

    cls& operator[](int dat, cls& c)
    {
        //...
        return c;
    }
     
    int main()
    {
        cls c(1, 'h');
        6[c];   // []是二元运算符,左侧是6,右侧是c
        return 0;
    }
    

       因为[]操作符重载函数是全局的,也就是没有了该函数的左操作数是this指针的限制,程序员可以任意定义左操作数的类型,

       类似的,就会出现6=c, 6(c), 6->c的代码,显然这样的代码是错误的。

       故:C++中不能用友员函数重载的运算符有:=  ()  []  ->

    6. 重载()操作符

       x 是类 X 的一个对象,则表达式 x(arg1,arg2,… ) 可被解释为 x.operator()(arg1,arg2,…)

       ()也是二元运算符。

    class F
    { 
    public:  
        double operator()(double x, double y)
        {
            return x * x + y * y ;
        }
    };
    
    int main()           
    { 
        F f;
        cout << f(5.2, 2.5) << endl;  // f.operator()(5.2, 2.5)
        return 0;
    }
    
  • 相关阅读:
    函数声明与函数指针
    【LeetCode】三角形最小路径和
    【LeetCode】字符串中的第一个唯一字符
    【LeetCode】基本计算器II
    【LeetCode】二叉树的最小深度
    【LeetCode】加油站
    java中json与对象的转换
    idea2017 无法使用maven3.6.3版本导入依赖
    springboot项目注册为windows系统服务并设置开机自启
    springmvc上传文件
  • 原文地址:https://www.cnblogs.com/yanghh/p/12945941.html
Copyright © 2020-2023  润新知