3.C++的部分基础知识简单小结之运算符重载


还记得 coutcin吗?

cin>>a;
cout<<a<<endl;

>>运算符在C语言中是右移运算符,用于整数,表示右移一位,相当于/2,<<是左移运算符,用于整数,相当于*2;

然而这两个运算符在这里却对输入输出流进行操作,显然已经不是二进制移位运算符了,这就是说,它们被赋予了新的功能.对系统原有的运算符赋予其它功能的过程,称为运算符的重载.

重载的规则

只能对系统已有的运算符进行重载,不能自定义运算符,且除去以下五个运算符不能重载,其余都可以重载:

  • 域运算符::
  • 成员访问运算符.
  • 成员指针访问运算符*
  • sizeof运算符
  • 三目元运算符? :

重载不改变运算符操作数的个数

重载不改变运算符优先级

重载不改变运算符的结合性

重载运算符的函数不能带有默认参数

重载运算符的操作数至少有一个是自定义类,不能全都是标准类型(int,double等)

=&不必重载,适用于每一个类,但是重载=在某些情况下相当于重载拷贝构造函数(较冷门)

运算符重载的方法

使用operator关键字进行运算符重载,其格式一般如下:

返回类型 operator 运算符名称(参数列表) {
    实现自定义功能的语句;
}

例如我们对Complex类型进行+重载声明:

Complex operator+(Complex &a, Complex &b);

重载为成员函数

将运算符重载为成员函数时,可省略一个参数,用this代替当前对象,如:

#include<iostream>
#include<string>
using namespace std;

//Complex.h;
class Complex {
  public:
    Complex(double,double);
    Complex operator+(Complex&);
    void show();
  private:
    double real;
    double imag;
};

//Complex.cpp;
Complex::Complex(double r,double i):real(r),imag(i){}

Complex Complex::operator+(Complex &c){
    return Complex(this->real+c.real,this->imag+c.imag);
}

void Complex::show() {
    if(imag >= 0)
        cout<<real<<"+"<<imag<<"i"<<endl;
    else
        cout<<real<<imag<<"i"<<endl;
}

//main.cpp;
int main() {
    Complex c1(10,18),c2(-2,2);
    Complex c3 = c1+c2;
    c3.show();
    return 0;
}

重载函数调用是隐式调用,c1+c2相当于c1.operator+(c2).

注:省略的参数永远都是运算符左边第一个参数,c1+c2,省略的是c1

重载为友元函数

重载为友元函数时,由于不是类的成员函数,因此无法省略一个参数,如:

//Complex.h;
class Complex {
  public:
    Complex(double,double);
    friend Complex operator+(Complex&,Complex&);
    void show();
  private:
    double real;
    double imag;
};

//Complex.cpp;
Complex operator+(Complex &c1,Complex &c2) {
    return Complex(c1.real+c2.real,c1.imag+c2.imag);
}

可以看出其实这两种重载区别不大,只是参数的个数上的区别,但很多时候重载为友元运算符比成员运算符更为有效,一般可以采用以下的建议(非硬性规定):

重载方式 适用运算符类别
成员函数 单目元运算符
友元函数 双目元运算符

重载单目元运算符

以上说的都是双目元运算符的重载,现在我们来看看对单目元运算符的重载,我们一般使用成员函数重载.

前缀和后缀式的区别

我们知道++ii++的区别,是先自增,再使用还是先使用,再自增.那么在重载运算符的时候,也会遇到这个问题.这就是说,对于一个单目元运算符,我们至少要写两种重载形式——前缀式和后缀式.下面以++运算符为例,介绍两种声明形式:

前缀式:

类名 类名::operator++();

后缀式:

类名 类名::operator++(int);

注:二者相差一个int参数是C++所规定的,实际使用并不需要传递参数,编译系统会根据单目元运算符的位置确定调用哪个函数.


下面我们写一个含有hour和minute的Time类,完成对++--的两种形式重载:

#include<iostream>
#include<string>
using namespace std;

//Time.h;
class Time {
  public:
    Time();
    Time(int,int);
    Time operator++();
    Time operator++(int);
    Time operator--();
    Time operator--(int);
    void show();
  private:
    int hour;
    int minute;
};

//Time.cpp;
Time::Time() {
    hour = 0;
    minute = 0;
}

Time::Time(int h,int m):hour(h),minute(m){}

Time Time::operator++() {
    if(++minute >= 60) {
        minute -= 60;
        if(++hour >= 24) {
            hour -= 24;
        }
    }
    return *this;
}

Time Time::operator++(int) {
    Time temp = *this;
    if(++minute >= 60) {
        minute -= 60;
        if(++hour >= 24) {
            hour -= 24;
        }
    }
    return temp;
}

Time Time::operator--() {
    if(--minute < 0) {
        minute = 59;
        if(--hour < 0) {
            hour = 23;
        }
    }
    return *this;
}

Time Time::operator--(int) {
    Time temp = *this;
    if(--minute < 0) {
        minute = 59;
        if(--hour < 0) {
            hour = 23;
        }
    }
    return temp;
}

void Time::show() {
    cout<<hour<<":"<<minute<<endl;
}

//main.cpp;
int main() {
    Time t1(23,59),t2(0,0);
    Time tmp = t1++;
    Time temp = ++t1;
    temp.show();
    tmp.show();
    temp = t2--;
    tmp = --t2;
    temp.show();
    tmp.show();
    return 0;
}

注:C++特别提供了成员函数重载以区分单目元运算符前缀后缀式的方法,请不要再使用友元类重载了!

重载>><<运算符

在编写程序的过程中,输入和输出总是一件很重要的事情,基于这个原因,我们会想使用cincout直接输入/输出自定义的类型,但是这两个函数只能识别基本类型,因此我们需要对>>和<<进行重载,以达到使用cin和cout输入/输出自定义类的目的.


只能将>><<的重载函数作为友元,而不能作为类的成员函数,这是因为,C++对其重载的格式的要求,如下:

istream& operator>>(istream&,自定义类名&);
ostream& operator<<(ostream&,自定义类名&);

注:其中istreamostream分别是输入流和输出流,是标准类库的两个类,用户无法更改.

按照成员函数重载省略左边第一个参数的原则,即使要写成成员函数重载,也只能写成istreamostream的成员函数,这显然是无法做到的,因此只能重载为友元函数.

下面实现一个简单例子:

#include<iostream>
#include<string>
using namespace std;

//Time.h;
class Time {
  public:
    Time();
    friend istream& operator>>(istream&,Time&);
    friend ostream& operator<<(ostream&,Time&);
    void show();
  private:
    int hour;
    int minute;
};

//Time.cpp;
Time::Time() {
    hour = 0;
    minute = 0;
}

istream& operator>>(istream &input,Time &t) {
    input>>t.hour>>t.minute;
    return input;
}

ostream& operator<<(ostream &output,Time &t) {
    output<<t.hour<<":"<<t.minute<<endl;
    return output;
}

//main.cpp;
int main() {
    Time t;
    cin>>t;
    cout<<t;
    return 0;
}

注:请务必按照要求,将>><<重载为友元运算符.

小结

  • 运算符不止可以重载为友元、成员函数,还可以是普通函数
  • 对于第一点,由于类的封装性特点,普通函数可能无法访问一些数据成员,因此不常用
  • 运算符既可重载为友元函数,也可以重载为成员函数;
  • 成员函数少写第一个参数,且该参数必然是本类对象类型;
  • 对于重载为友元函数的运算符,建议使用引用;
  • 单目元运算符只用成员函数重载
  • 双目元运算符建议使用友元函数重载
  • 双目元运算符重载函数,并不一定要求两个参数是同一类型
  • 对于>><<的重载,只能重载为友元函数,且有格式要求

以上就是运算符重载的全部内容

猜你喜欢

转载自blog.csdn.net/AAMahone/article/details/85108343