前面回顾了C++的友元函数及友元类的相关要点。本文主要讲述运算符的重载,它也与友元有部分的关联。为什么在C++中需要进行运算符重载呢?主要有下面几个原因:
(1) 对于标准类型的数据,如int, double都可以进行加、减、乘、除、大于、小于比较等运算,这是很直观的。而C++的类对象默认是没有这些功能的。如果要实现类对象的这些运算,可以自己定义成员函数,通过函数调用的方式来实现,如obj1.Add(obj2)这种方式不直观,而如果能实现obj1 = obj1 - obj2; 这与标准类型的数据计算方式是一样的,而这是我们最容易接受的,因为很熟悉,所以一看便知。所以,在C++中,运算符重载允许把一些标准的运算符(加、减、乘、除、大于、小于比较等运算)应用于自定义类型的类对象中。
(2) 程序员阅读程序时,直观,自然,提高可读性。
(3) 此外,可以体现C++的可扩充性。
运算符重载虽然有好处,但是也需要注意一些要点:
(1) 运算符重载只是语法上的方便,它只是另一种函数调用的方式
(2) 运算符重载的本质还是函数重载
(3) 不要滥用重载,只有在涉及的代码更容易写,尤其是更易读时才有必要重载。
运算符重载的实现方式有2种:成员函数重载和非成员函数重载(友元函数重载),
成员函数重载的原型: 函数类型 operator运算符(参数表)
成员函数定义的格式:函数类型 类名::operator运算符(参数表){函数体;}
非成员函数重载的原型:friend 函数类型 operator运算符(参数表)
非成员函数定义的原型:函数类型 operator运算符(参数表){函数体;}
下面举一个复数类complex重载的例子:
#ifndef _COMPLEX_H
#define _COMPLEX_H
class complex
{
public:
complex(int real, int imag):real_(real),imag_(imag){}
complex();
~complex();
complex& Add(const complex& other);
void Display() const;
complex operator+(const complex& other); //成员函数重载
friend complex operator+(const complex& c1, const complex& c2); //友元函数重载
private:
int real_; //复数的实部
int imag_; //虚部
};
#endif
下面是类的具体实现:
#include <iostream>
#include "complex.h"
using namespace std;
complex::complex(){cout << "complex..."<<endl;}
complex::~complex(){cout << "~complex"<<endl;}
void complex::Display() const
{
cout <<real_<<"+"<<imag_<<"i"<<endl;
}
complex& complex::Add(const complex& other)
{
real_ += other.real_;
imag_ += other.imag_;
return *this;
}
//成员函数重载+运算符的具体定义
complex complex::operator+(const complex& other)
{
int r = real_ + other.real_;
int i = imag_ + other.imag_;
return complex(r,i);
}
//友元函数+运算符重载的具体定义:
complex operator+(const complex& c1, const complex& c2)
{
int r = c1.real_ + c2.real_;
int i = c1.imag_ + c2.imag_;
return complex(r,i);
}
下面写个简单的测试用例:
int main()
{
complex c1(3,5);
complex c2(4,6);
c1.Add(c2); //c1的值发生了变化
c1.Display();
//等价于c1.operator+(c2)或operator+(c1,c2)
complex c3 = c1 + c2; //此方法c1,c2值不变,将加的结果初始化为c3.
}
需要注意一点:complex c3 = c1 + c2,它与c1.operator+(c2)或operator+(c1,c2)是一样的,也就是说,c3 = c1 + c2将调用后面两个表达式的其中一个,当运算符的成员函数与非成员函数重载时,它调用的是哪一个呢?答案是优先调用成员函数重载。如果成员函数重载没提供,只提供了友元函数重载,则将调用友元函数重载。
下面简单说明一下运算符重载的规则:
(1) 不允许发明新的运算符
(2) 不能改变运算符操作对象的个数
(3) 优先级与结合性不变
(4) 不能重载的运算符有 :: ?: .(点,即域直接成员访问) *(类成员指针引用) sizeof
(5) 单目运算符最好重载为类的成员函数,双目运算符则重载为类的友元函数(但=、()、[]、->除外)。
(6) 流类型转换运算符只能以成员函数方式重载
(7) 流运算符只能以友元的方式重载
上面第(5)点中,为何有些双目运算符特例要重载为类的成员函数,下面来看一个简单的例子。