运算符重载

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

 

posted @ 2020-05-24 09:58  _yanghh  阅读(429)  评论(0编辑  收藏  举报