C++——运算符重载

运算符重载

什么是运算符重载?
所谓重载,就是赋予新的含义。
函数重载可以让一个函数名有多种功能,在不同情况下进行不同的操作。
运算符重载也是一个道理,同一个运算符可以有不同的功能。

实际上,我们已经在不知不觉中使用了运算符重载。例如,+号可以对不同类型(int、float 等)的数据进行加法操作;<<既是位移运算符,又可以配合 cout 向控制台输出数据。C++ 本身已经对这些运算符进行了重载。

运算符重载的本质是函数重载:
运算符重载其实就是定义一个函数,在函数体内实现想要的功能,当用到该运算符时,编译器会自动调用这个函数,由这个函数实现其想要的功能。

好处:
虽然运算符重载所实现的功能完全可以用函数替代,但运算符重载使得程序的书写更加人性化,易于阅读。运算符被重载后,原有的功能仍然保留,没有丧失或改变。通过运算符重载,扩大了C++已有运算符的功能,使之能用于对象。

重载的注意事项:

  • 重载不能改变运算符的优先级和结合性

  • 重载不会改变运算符的用法,原有有几个操作数、操作数在左边还是在右边,这些都不会改变。例如+号总是出现在两个操作数之间,重载后也必须如此。

可以重载和不能重载的运算符

在这里插入图片描述

为什么不能

“?:”

假设可以重载,那么我们来看下列的代码:

exp1 ? exp2 : exp3

该运算符的含义是执行exp2和exp3中的一个,假设重载了,无法保证该运算符只执行某一个表达式的语义了,该运算符的跳转性质就不复存在了。所以,“?:”不能被重载。

“.”

假设可以重载,我们可以假设一种情况,创建一个对象,调用该对象的函数。
下面例子中,x.fun()就不知道是调用X还是Y的fun函数了。
“.”运算符的含义是引用对象成员,然而被重载后就不能保证了,导致运算符意义的混淆。

class Y{
 void fun();
};
class X {
public:
    Y *p;
    Y &operator.(){
        return *p;
    }

    void fun();
};

void g(X &x) {
    x.fun();
}

“::”

该运算符只是在编译的时候域解析,而没有运算参与。根据重载的规则,如果重载该运算符,就赋予了新的语义,可能会出现混淆。

“sizeof”

不能被重载的原因主要是内部许多指针都依赖sizeof。

以成员函数还是普通的非成员函数的形式重载运算符?

下面的准则有助于我们做出抉择:

  • 赋值(=)、下标([ ])、调用(( ))和成员访问箭头(->)运算符必须是成员。
  • 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员。
  • 具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数。
    所以 +、-、*、/、==、!= 应该以普通的非成员函数进行重载
  • 复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。所以 +=、-=、*=、/= 应该以成员函数进行重载

下面举例解释:

1、为什么要以全局函数的形式重载 +、-、*、/、==、!=
看下面的例子:

#include <iostream>
using namespace std;
//复数类
class Complex{
public:
    Complex(): m_real(0.0), m_imag(0.0){ }
    Complex(double real, double imag): m_real(real), m_imag(imag){ }
    Complex(double real): m_real(real), m_imag(0.0){ }  //转换构造函数
public:
    friend Complex operator+(const Complex &c1, const Complex &c2);
public:
    double real() const{ return m_real; }
    double imag() const{ return m_imag; }
private:
    double m_real;  //实部
    double m_imag;  //虚部
};
//重载+运算符
Complex operator+(const Complex &c1, const Complex &c2){
    Complex c;
    c.m_real = c1.m_real + c2.m_real;
    c.m_imag = c1.m_imag + c2.m_imag;
    return c;
}
int main(){
    Complex c1(25, 35);
    Complex c2 = c1 + 15.6;
    Complex c3 = 28.23 + c1;
    cout<<c2.real()<<" + "<<c2.imag()<<"i"<<endl;
    cout<<c3.real()<<" + "<<c3.imag()<<"i"<<endl;
   
    return 0;
}

运行结果:

40.6 + 35i
53.23 + 35i

上面的例子中,我们定义的operator+是一个全局函数而不是成员函数,这样做是为了保证 + 运算符的操作数能够被对称的处理;换句话说,double 类型在 + 左边和右边都是正确的。第 30 行代码中,15.6 在 + 的右边,第 31 行代码中,28.23 在 + 的左边,它们都能够被顺利地转换为 Complex 类型,所以不会出错。

如果将operator+定义为成员函数,根据“+ 运算符具有左结合性”这条原则,Complex c2 = c1 + 15.6;会被转换为下面的形式:

Complex c2 = c1.operator+(Complex(15.6));

这就是通过对象调用成员函数,是正确的。

而对于Complex c3 = 28.23 + c1;,编译器会尝试转换为不同的形式:

Complex c3 = (28.23).operator+(c1);

很显然这是错误的,因为 double 类型并没有以成员函数的形式重载 +。

也就是说,以成员函数的形式重载 +,只能计算c1 + 15.6,不能计算28.23 + c1,这是不对称的

2、为什么要以成员函数的形式重载 +=等运算符
我们首先要明白,运算符重载的初衷是给类添加新的功能,方便类的运算,它作为类的成员函数是理所应当的,是首选的。
只有类似上面+运算符不能对称地处理数据时才以全局函数的形式进行重载、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值