学科分类
目录
C++基础

输入输出运算符的重载

运算符重载一般有两种形式:重载为类的成员函数和重载为类的友元元函数。接下来我们就对这两种重载形式进行详细讲解。

1、重载为类的成员函数

在上一小节重载“+”、“-”运算符时,就是重载成了类的成员函数。重载为成员函数,它就可以自由地访问本类的数据成员。运算的操作数会以调用者或参数的形式表示。

如果是双目运算符重载为类的成员函数,则它有两个操作数,左操作数是对象本身的数据,由this指针指出,右操作数则通过运算符重载函数的参数表来传递。其一般调用格式如下所示:

左操作数.运算符重载函数(右操作数);

如上一节案例中重载“+”运算符,当调用a1+a2时,其实就相当于函数调用a1.oprerator+(a2)。

如果是单目运算符重载为类的成员函数,则要分为前置与后置运算符,如果是前置运算符,则它的操作数是函数调用者,函数没有参数,其一般调用格式如下所示:

操作数.运算符重载函数();

如上一节案例中如果重载前置单目运算符“++”,则++a1的调用其实就相当于函数调用a1.operator++()。

如果是后置单目运算符,则函数要带一个整形参数,这个整型参数不起任何作用,只是用来区分前置与后置。

为了加深读者的理解,接下来通过一个案例来演示前置“++”与后置“++”运算符的重载,具体代码如例1所示。

例1

 1    #define  _CRT_SECURE_NO_WARNINGS
 2    #include <iostream>
 3    using namespace std;
 4    class A
 5    {
 6    private:
 7        int x;
 8        int y;
 9    public:
 10        A(int x1 = 0, int y1 = 0):x(x1), y(y1){}
 11        void show() const; //输出数据
 12        A operator++();  //重载前置++
 13        A operator++(int); //重载后置++
 14    };
 15    
 16    void A::show() const
 17    {
 18        cout << "(x,y) = " << "(" << x << "," << y << ")" << endl;
 19    }
 20    A A::operator++()  //前置++实现
 21    {
 22        ++x;
 23        ++y;
 24        return *this;
 25    }
 26    A A::operator++(int) //后置++实现
 27    {
 28        A a = *this;
 29        ++(*this);  //调用已经实现的前置++
 30        return a;
 31    }
 32    int main()
 33    {
 34        A a1(1, 2), a2(3, 4);
 35        (a1++).show();
 36        (++a2).show();
 37    
 38        system("pause");
 39        return 0;
 40    }

运行结果如图1所示。

图1 例1运行结果

在例1中创建了两个对象a1,a2,a1调用后置“++”,a2调用前置“++”,由图4-3打印结果所知,a1先打印出了结果后执行“++”,而a2先执行了“++”运算后打印了结果。

在实现前置“++”时,数据成员进行自增运算,然后返回当前对象(即this指针所指向的对象)。而实现后置“++”时,创建了一个临时对象来保存当前对象的值,然后再将当前对象自增,最后返回的是保存了初始值的临时对象。

注意:前置单目运算符与后置单目运算符重载的最主要区别是函数的形参,后置单目运算符带一个int型形参,但它只起区分作用。

2、重载为类的友元函数

除了可以重载为类的成员函数外,运算符还可以重载为类的友元函数,重载为类的友元函数只是在函数前加一个friend关键字,其格式如下所示:

friend 返回类型 operator 运算符(参数列表)
{
  函数体;
}

重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右保持一一对应。

重载为类的友元函数,则调用运算符的一般格式如下所示:

operator 运算符(参数1,参数2);

例如调用a1+a2其实相当于函数调用operator+(a1,a2)。

为了加深读者的理解,接下来改写例4-1,将“+”、“-”运算符重载为类的友元函数,具体代码如例2所示。

例2

 1    #include <iostream>
 2    using namespace std;
 3    class A
 4    {
 5    private:
 6        int x;
 7        int y;
 8    public:
 9        A(int x1 = 0, int y1 = 0):x(x1), y(y1){}
 10        void show() const; //输出数据
 11        friend A operator+(const A& a1, const A& a2) ; //重载为类的友元函数
 12        friend A operator-(const A& a1, const A& a2); //重载为类的友元函数
 13    };
 14    void A::show() const
 15    {
 16        cout << "(x,y) = " << "(" << x << "," << y << ")" << endl;
 17    }
 18     A operator+(const A& a1, const A& a2)
 19    {
 20        return A(a1.x + a2.x, a1.y + a2.y);
 21    }
 22     A operator-(const A& a1, const A& a2)
 23    {
 24         return A(a1.x - a2.x, a1.y - a2.y);
 25    }
 26    int main()
 27    {
 28        A a1(1, 2);
 29        A a2(4, 5);
 30        A a;
 31        cout << "a1: "; 
 32         a1.show();
 33        cout << "a2: "; 
 34         a2.show();
 35        a = a1 + a2;
 36        cout << "a: "; a.show();
 37    
 38        a = a1 - a2;
 39        cout << "a: "; a.show();
 40    
 41        system("pause");
 42        return 0;
 43    }

运行结果如图2所示。

图2 例2运行结果

例2将“+”、“-”运算符重载为类A的友元函数,则运行结果和例1相同,证明两种重载形式都可以完成运算符的重新定义。

在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:

(1)一般情况下,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。

(2)若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。

(3)若运算符的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。

(4)具有对称性的运算符可能转换任意一端的运算对象,如算术、关系运算符等,通常重载为非成员函数。

(5)有4个运算符必须重载为类的成员函数:赋值(=)、下标([])、调用(())、成员访问箭头(->)。

点击此处
隐藏目录