深入理解 运算符的重载

今天看到这么一道题:

template<class T> class BigNumber{ 
    long n; 
public: 
    BigNumber(T i):n(i){}
    BigNumber operator+(BigNumber b)
    { 
        return BigNumber(n+b.n); 
    } 
};

已知b1,b2是BigNumber的两个对象,则下列表达式中错误的是?

A 3+3
B b1+3
C b1+b2
D 3+b1

题干中的意思不就是类里面重载了一个“+”法运算符么,所以:
A 是正常的算数加法操作 √
C 是调用了重载的“+”号运算符,实际执行起来就类似于这样:b1.operator+(b2) √

接下来重点是B和D哪个正确?

经过查资料,分析如下:
1、首先,题干中的重载运算符是作为类的成员函数的,作为类的成员函数时,如果被重载的运算符是二元运算符的话,运算符重载函数只需要一个参数,因为另一个参数是隐含的this指针;
2、加法运算符的结合型是自左向右,所以对于B来讲,执行时编译器发现b1是一个BigNumber的对象,就会去调用重载的“+”法运算符,类似于:b1.operator(3),此时的3被强制转化成BigNumber类型;所以C √
3、对于D,执行时,编译器首先检查“+”的左边,发现是一个普通int型变量,所以就不去调用重载的“+”法运算符函数了。但是“+”号右边又不是一个基本算数类型,所以,就报错了。所以 D ×

当重载运算符作为类的成员函数时,关键点在 重载的运算符 的结合型

1、对于上面讨论的题目,“+”,结合型从左至右,所以先检查“+”号左边是否是一个BigNumber对象,如果是,就执行重载函数,否则,不执行。
所以,结合型在重载时是很重要的,例如:

//秒表类
class stopwatch{
public:
    stopwatch(): m_min(0), m_sec(0){ }
public:
    void setzero(){ m_min = 0; m_sec = 0; }
    stopwatch run();  // 运行
    stopwatch operator++();  //++i,前置形式 ?
    stopwatch operator++(int);  //i++,后置形式 ?
    friend ostream & operator<<( ostream &, const stopwatch &);
private:
    int m_min;  //分钟
    int m_sec;  //秒钟
};

stopwatch operator++();这个是++运算符的前置重载形式,为什么?因为++运算符的结合型是自右至左,而重载函数无参数。若有以下定义

stopwatch A;
++A; //此时A就作为一个默认参数,类似于这样A.operator+();
A++;  // ??据说是 强行规定!! -_-!!

我是这么记忆的:
自右向左检查,我猜测编译器自动的给A++补了个零,就变成0A++了,类似于这样A.operator(0);-_-!!
其实通常情况下,重载++运算符是这样的:

(1)前置++运算符的重载方式:
成员函数的重载: 函数类型& operator++()
友元函数的重载:friend 函数类型& operator++(类类型& )

(2)后置++运算符的重载方式:
成员函数的重载:函数类型 operator++(int)
友元函数的重载:friend 函数类型 operator++(类类型&, int)

需要注意的一点是,后置++返回的是右值,所以返回类型不要再加&引用了。友元函数的重载和普通函数的重载是一样的。
(这段参考:https://blog.csdn.net/friendbkf/article/details/45644891

所以++++c合法,而c++++不合法。为什么?

++重载函数的实现大致是这样的:

ClassA& ClassA::operator++()            //前置++
{     
    itsVal++;   
    return *this;   
}    
const ClassA ClassA::operator++(int)    //后置++
{       
    ClassA temp(*this);   
    itsVal++;   
    return temp;   
}   

如果要实现c++++合法,必须使后置返回变量或变量的引用。c++是先返回c值再+1,所以不可能返回c,那就只能先建立局部变量来保存c的初值,然后再返回局部变量(局部变量不允许返回引用),但返回了局部变量之后,如果再连着进行下一次++运算,参与运算的就是这个局部变量的值了,所以此时c++++其实等效与c++,也就没有存在的意义了。

重载函数作为全局函数进行重载,运算符的结合性同样很重要

下面我们以全局函数的形式重载>>,使它能够读入两个 double 类型的数据,并分别赋值给复数的实部和虚部:

istream & operator>>(istream &in, complex &A){  //comples是先前定义的一个表示复数的类;
    in >> A.m_real >> A.m_imag;
    return in;
}

istream 表示输入流,cin 是 istream 类的对象,只不过这个对象是在标准库中定义的。之所以返回 istream 类对象的引用,是为了能够连续读取复数,让代码书写更加漂亮,例如:

complex c1, c2;
cin >> c1 >> c2;

为什么我们把istream 类型作为” operator>> “函数的第一个参数?(如果简单的把operato>> 的两个参数反过来是会报错的),因为cin在运算时的结合型是自左相右,在执行cin>>c1时,就类似于:operator>>(cin, c1);

如果我们真想把 operator>> 函数的两个参考颠倒过来,即:istream & operator>>(complex &A, istream &in),那么我们在使用cin进行标准输入时,就只能这样了:

c1 >> cin;  //只进行单次输入
c2 >> (c1 >> cin); //进行连续输入;

对于c1 >> cin; 执行起来类似于这样的:operator>> (c1, cin);
对于c2 >> (c1 >> cin); 执行起来类似于这样的:operator>>(c2, (c1 >> cin));
是不是很别扭?所以呢,别浪,还是老老实实的这么写吧:istream & operator>>(istream &in, complex &A) {·····}
这一段参考:(http://c.biancheng.net/cpp/biancheng/view/3016.html

猜你喜欢

转载自blog.csdn.net/baidu_35679960/article/details/80744409