重载运算符

重载运算符是C++语言的优点之一,对已有的运算符进行重载,赋予其另一种功能,以适应不同的数据类型。运算符重载的声明方式是关键字operator+相关运算的符号。
运算符的重载事实上是在定义函数:
<返回类型说明> operator<运算符符号>(参数列表)
{
函数体;
}

运算符的重载要遵循以下规则:
1.除了类属关系运算符 ’ . ‘, 成员指针运算符 ’ * ‘, 作用域运算符 ‘::’ , sizeof运算符,以及三目运算符以外,C++中所有的运算符都可以被重载。
2.重载运算符限制在C++语言中已有的运算符范围之内的允许重载的运算符之中,不能创建新的运算符。
3.运算符重载的实质是函数的重载,因此编译过程中对运算符重载的选择,遵循函数重载的选择原则。
4.重载之后的运算符不能改变运算符的优先级和结核性,也不能改变运算符的操作数和语法。
5.运算符重载不能改变运算符内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用。
6.运算符重载是针对新类型数据的实际需要对原有的运算符进行适当的改造,重载的功能应当与原有的功能相似,避免没有目的地使用重载运算符。

运算符的重载一般有两种形式,重载为类的成员和重载为类的非成员函数。非成员函数通常是友元函数(比如重载输出流)
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数少一个(*this指针隐式存在,成员函数用this指针隐式地访问了类的一个对象,它充当了运算符的最左边的操作数)
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数没有变化,所有操作数都必须通过函数的形参进行传递,函数的参数与操作数目一一对应。
下面给出重载各类运算符的实例。

#include<iostream>
using namespace std;

class Int
{
public:
  Int(int i = 0):m_i(i)  //定义构造函数,相当于将m_i(i)写到下面的{}中
  {}
  ~Int()
private:
  int m_i;
};

我们构造了一个类Int,类里有int型的m_i。我们通过重载运算符要达到对Int对象的操作要和对int整形操作一样。
我们通过代码分别实现 + - * / % , += -= *= /= , Int++, ++Int, -(取负数), >> <<, && || , & | , new delete,以及重载输出流。

#include<iostream>
using namespace std;
class Int
{
public:
  Int(int i = 0):m_i(i)  
  {}
  ~Int()
public:
    Int  operator+(const Int &i);  
    void operator+=(const Int &i);
    Int operator%(const Int &i);
    void operator%=(const Int &i);
    Int operator++(int);        //Int++
    Int operator++();           //++Int 
    Int operator-();        
    bool operator>=(const Int &i);
    Int operator>>(int &i);
    Int operator&(const Int &i);
    int operator|(const Int &i);
    bool operator&&(const Int &i);
    bool operator||(const Int &i);
    void *operator new(size_t sz);
    void operator delete(void *p);
private:
  int m_i;
};

“+ - * /”相似,以+为例:

Int& Int::operator+(const Int &i)  
{
    return i.m_i + m_i;
}

在类外实现函数要加上类的作用域(Int::)防止二异性。在参数列表中隐藏包含了this指针,所以参数列表可以还原为(*this, const Int &i)。this指针是指向当前对象的意思,在这个代码中,operator前面的对象就是this指针所指,后面的对象就是传进来的参数 i 。两个Int类型的数据相加,实际定义为Int类内部的m_i相加并返回Int型的数据 : i.m_i + m_i 。

int main()
{
    Int a = 1;
    Int b = 2;
    Int c = 3;

    cout << c = a + b << endl;
}

如果这样直接输出的话,程序会报错:don`t define < < . 我们还要对Int类型如何输出进行定义。

class Int;  //写在class之前,要声明为友元前要先定义class
ostream& operator<<(ostream &out, const Int &i);//定义重载输出<<的函数,注意这里可没有*this
friend ostream& operator << (ostream &out, const Int &i);//在class内部开头,声明为友元,这样就可以调用私有成员m_i了。
ostream& operator<<(ostream &out, const Int &i)//在class外部实现<<的重载,输出i.m_i
{
    out << (i.m_i);
    return out;
}

这样我们就可以编译通过了,输出结果是: 3 ,对自己定义的Int类也能进行加法运算啦。

tips以前有这么一道面试题,问可不可以将 输出流<< 当做类的成员函数进行重载?
答案是可以的,但事实上没有人这么做。因为如果作为成员函数就会引入this指针,输出时要靠对象去驱动函数(等同于+ - * /),那么输出<<的操作就会由 cout << a ; 变为 a cout ; .这样的方式与我们平常的习惯不同,违反了重载的原则。
我们再来看一下取余%运算符的重载:

Int& Int::operator%(const Int &i)
{
    return m_i % i.m_i;
}

和+很类似,在内部实际进行的是m_i之间的运算。
tips大家有没有注意到声明时Int后面的 &,这个符号是什么作用呢?在常规的函数中,当返回一个值时函数的生命也就终结了,它会被析构函数析构掉,那么这个值是怎么保存的呢?实际上函数提前会将这个值存入某个不知名内存中,当另个程序需要返回值时,它来传递返回值,传递完在被释放掉。程序运行过程中申请一个空间在释放掉,无疑是耗费时间的。而&得作用就是延长返回值的生命,直到返回值被接受,这样我们就避免了多余的申请释放空间所花费的时间,提高了程序的运算速度。

我们来看看+= 这类运算符的重载。

void Int::operator+=(const Int &i)
{
    m_i = m_i + i.m_i;
}

重载运算符时要注意运算符的性质,a = a + b; 和 a += b; 在重载运算符时是不同的,+ 的重载需要返回值,但 += 不需要返回值,因为是直接对this指针进行操作的。
下来我们再来看看比较复杂的两个 Int++ 和 ++Int。

Int Int::operator++()          //++a
{
    m_i++;
    return *this;
}

++a先加再计算,this的m_i++,在返回this指针。

Int& Int::operator++(int )      //a++
{
    Int temp(m_i);
    m_i++;
    return temp;
}

a++由于先运算在++,所以我们得定义Int型的临时类,将this指针的m_i++,返回临时类temp。
a++在这里的参数要求有一个整型int,++a不需要,这是编译器规定的,用于区分++运算符是前++还是后++。

当Bool类型的运算符重载时,比如> ,< ,>= , <=, 要符合运算符的性质。

bool Int::operator&&(const Int &i)
{
    if(m_i != 0 && i.m_i != 0){
    return true;
    }else{
    return false;
    }
}
bool Int::operator||(const Int &i)
{
    if(m_i == 0 && i.m_i == 0){
    return false;
    }else{
    return true;
    }
}

在来看看new,delect运算符的重载。

void* Int::operator new(size_t sz)
{
    cout << "operator new" << endl;
    void *p = malloc(sz);
    return p;     
}
void Int::operator delete(void *p)
{
    free(p);
}

再补充一点,当定义一个类后,如何用类对象直接给整型赋值?

class A
{
private:
    int m_i = 10;
};
int a = A; //怎么赋值?

技巧是重载int!

operator int()const
{
    return data;
}

运算符重载的所有形式都已经介绍完毕,将运算符作为函数一样的调用,使得我们运用类型的语法时更加灵活。
重载运算符需要掌握的的知识点:
1.运算符重载的实质。
2.学会处理重载时的参数。
3.学会分清不同运算符重载时的返回值。
4.搞清运算符时成员函数还是友元函数,和两者的区别。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值