新手向C++运算符重载

运算符重载

介绍C++的运算符重载及其原理,梳理一下注意要点,融入自己的理解,用一个例子贯穿,学浅勿喷。

1、引入

使用一个例子引入运算符重载。

首先,定义一个复数类Complex

class Complex{
private:
    int real;
    int virt;
public:
    Complex(int r, int v){real=r;virt=v;}
}

现在假设我们要实现两个复数相加,没有运算符重载之前,可以增加一个成员函数add:

Complex Complex::add(const Complex & c) const {
    return Complex(real+c.real, virt+c.virt);
}

可以调用add函数实现复数相加:c3 = c1.add(c2),但是如果能使用c3 = c1 + c2的表达式形式,显然更便捷易懂。这里就需要运算符重载,重载运算符之后,就能直接使用运算表达式对自定义类型进行运算。

运算符重载通过运算符函数实现,这是一类特别的函数,函数名必须是operator关键字后跟要重载的运算符,参数列表与运算符的操作数相关(如何相关在后面说),其它的与普通函数一样。上面的add()函数,我们只需要修改函数名为operator+就成功重载了+运算:

Complex Complex::operator+(const Complex & c) const {
    return Complex(real+c.real, virt+c.virt);
}

现在,就可以使用c3=c1+c2来进行两个复数相加了。另外也可以使用函数调用c3=c1.operator+(c2)

总结就是重载运算符可以支持自定义类型进行运算符运算,为编程提供便利。

2、深入

可以看到,运算符重载本质上是函数重载,函数重载就是对同一个函数名对不同的参数列表有不同的功能,运算符重载也是使一个运算符对不同的操作数有不同的运算逻辑。更进一步,上面c1+c2c1.operator+(c2)是等效的,可以猜想,重载的运算符的最终是通过对应的运算符函数实现的。

那么,运算符与运算符函数具体是如何转化的?首先,运算符匹配很简单,运算符匹配直接找对应operator后面的运算符同样的函数即可。关键在操作数和参数列表的匹配,参数的匹匹配其实也很简单,就是单纯的按运算数从左到右作为参数代入而已(成员函数第一个参数是this)。比如,c1+c2c1.operator+(c2)等效,与c2.operator+(c1)不等效,即使计算结果一样。

重载一下*运算符实现复数倍乘,形如c1 * 3,增加一个如下成员函数:

Complex Complex::operator*(int n) const{
   return Complex(n*real, n*virt);
}

即可使用c2=c1*3实现复数倍乘。按运算习惯,有时候我们也会写成c2=3*c1,但这其实是错误的表达,因为*运算符并不存在参数是(int, Complex)的函数。所以必须再次重载运算符,注意这里第一个参数是int类型,成员函数不适用,只能用非成员函数。

//注意该函数应该为Complex的友函数,才能访问real,virl属性.
Complex operator*(int n,const Complex& c){
    return Complex(n*c.real, n*c.virt);
}

这样,就可以使用类似c2=3*c1的表达式了。

再次总结,运算符重载可以使用成员函数或非成员函数的方式,成员函数的第一个参数默认是所属类对象,如果运算符的第一个操作数是其它类型,必须使用非成员函数,且常常声明为友函数。

3、掌握

对运算符重载原理基本清楚之后,现在做下归纳,透彻掌握下。

3.1、运算符

首先,总结一下我们的主角们——运算符。并非所有的运算符都能重载,常见的能重载的运算符有

  1. 运算算术符+, -, *, /, %
  2. 关系运算符==<, >, <=, >=, !=
  3. 逻辑运算符&&, ||, !
  4. 位运算符&,|, ^,~, >>, <<
  5. 赋值运算符=, +=, -=, *=, /=,%=, &=, |=, ^=, <<=, >>=
  6. 单目运算符+(正), -(负), &(引用), *(指针), ++, --, new, new[], delete, delete[]
  7. 其它运算符[](下标), ->(成员访问), ()(函数调用),,(逗号)。

不能重载的运算符有:., .*, .->, ?:, ::以及sizeof类单词运算符。

3.2、限制

运算符重载有一定的限制:

  1. 操作数至少有一个为自定义类型。
  2. 运算符重载只能重载运算符的运算逻辑,无法改变运算符的固有特性。比如,无法自造运算符;无法改变运算符操作数数量,无法改变运算符优先级,无法改变运算符是左结合还是右结合,无法改变语法结构。
  3. 重载的运算逻辑应该合理,不应该重载+, 进行减操作,当然这点编译器无法判断。

4、实战

编写一些实例巩固一下,注意个别运算符重载的细节。

4.1、++/–的前置与后置。

自增(自减)虽然是单目,但是有前置或后置两种用法,所以默认前置,使用int参数标记后置。用成员函数的方式重载前置++实现实部加+,后置++实现虚部加1,都返回改变后的值。

   Complex& operator++(){ //前置++
        ++real;
        return *this;
    }
    Complex& operator++(int){//后置++
        ++virt;
        return *this;
    }

这里返回引用的目的是支持连续调用。如果使用非成员函数的方式,需要增加第一个参数类型为Complex,函数签名为:

Complex& operator++(Complex& c); //前置
Complex& operator++(Complex& c,int); //后置

4.2、输入输出

输入输出<<, >>本来就是由位运算重载过来的。为了支持我们输入与输出Complex类型数据,需要为它重载。因为第一个对象是std::cinstd::cout,所以只能用非成员函数的方式实现

istream& operator>>(istream& input, Complex& c){ //重载>>
    int r,v;
    char ch;
    input>>c.real;
    input>>ch;//过滤+
    input>>c.virt;
    input>>ch;//过滤j
    return input;
}
ostream& operator<<(ostream& output, const Complex& c){//重载<<
    output<

这里返回引用的目的也是支持连续调用,如cin>>c1>>c2;。重载之后,就可以实现输入输出,如:

Complex c1;
std::cin>>c1;  //输入形如1+2j
std::cout<

4.3、函数调用运算符()

()运算符其实是一个多元运算符,允许重载进行多个操作数的运算。 下面重载一个三个操作数(包括this)的。

    Complex& Complex::operator()(int a,int b){
        real+=a;
        virt+=b;
        return *this;
    }

重载之后,便可以使用c1(3, 4);实现实部加3虚部加4的运算。可以发现,()像是一个与对象同名的函数。

各个运算符的重载就不详举了…原理都差不多。想看更多例子,可以看菜鸟教程。完。

你可能感兴趣的:(C++)