3.运算符重载函数作为类成员函数和友元函数
对运算符重载的函数有两种处理方式:
<1>把运算符重载的函数作为类的成员函数
<2>运算符重载函数不是类的成员函数(可以是一个普通函数),在类中把它声明为友元函数
例子3:运算符“+”重载函数不作为成员函数,而放在类外,作为Complex类的友元函数
class Complex
{
public:
Complex(){ real = 0, imag = 0; } //定义构造函数
Complex(double r, double i){ real = r, imag = i; }//构造函数重载
friend Complex operator + (Complex &c1,Complex &c2); //重载函数为友元函数
void display();
private:
double real; //实部
double imag; //虚部
};
Complex operator + (Complex &c1,Complex &c2)
{
return Complex(real + c2.real, imag + c2.imag);
}
void Complex::display()
{
cout << real << "," << imag << "i" << endl;
}
int main()
{
Complex c1(3, 4), c2(5, -10), c3;
c3 = c1 + c2; //c1 + c2 解释为operator + (c1,c2);
cout << c3.display();
return 0;
}
备注:
有的C++编译系统(visual C++ 6.0)没有完全实现C++标准,它所提供的不带 .h 的头文件不支持把运算符重载函数作为友元函数。若遇到编译出错,可以将程序中
#include <iostream>
using namespace std;
改为
#include <iostream.h>
即可顺利运行,以后遇到类似情况,也可照此办理。
问题:为什么将运算符函数作为友元函数?
因为运算符函数要访问Complex类对象中的成员,如果运算符函数不是Complex类的友元函数,而是一个普通函数,它是没有权利访问Complex类的私有成员。
问题:什么情况下用成员函数方式?什么情况下用友元函数方式?
<1>如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。但是必须要求表达式中第一个参数(即运算符左侧的操作数)是一个对象,而且与运算符函数的类型相同。因此必须同那个类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。
<2>如果将运算符重载函数作为友元函数,如双目运算符重载为友元函数,由于友元函数不是类的成员函数,因此在函数的形参列表中必须有两个参数,不能省略,形参的顺序任意,不要求第一个参数必须为类对象。但在使用运算符的表达式中,要求运算符左侧的操作数与函数第1个参数对应,运算符右侧的操作数与函数的第2个参数对应。
如:在类Complex中声明
friend Complex operator + (int &i,Complex &c);
在类外定义友元函数:
Complex operator + (int &i,Complex &c)
{
return Complex(i + c.real, c.imag);
}
在调用运算符重载函数时
c3 = i + c2; //正确,类型匹配
c3 = c2 + i; //不正确,类型不匹配
数学上的交换律在此不适应。如果希望适用交换律,则应再重载一次运算符“+”,如:
Complex operator + (Complex &c, int &i)
{
return Complex(i + c.real, c.imag);
}
这样适用i + c2和c2 + i都合法,编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数。可以将以上两个运算符重载函数都作为友元函数;也可以将一个运算符重载函数(运算符左侧为对象名的)作为类的成员函数,另一个(运算符左侧不是对象名的)作为友元函数;但不能将两个都作为类的成员函数
究竟用哪个好?
由于友元的使用会破坏类的封装,从原则上讲,要尽量将运算符函数作为成员函数,以下仅供参考:
<1>C++规定,复制运算符“=”、下标运算符“[]”、函数调用运算符()、成员运算符“->”必须作为成员函数。
<2>流插入“<<”和流提取运算符“>>”、类型转换运算符不能定义为类的成员函数,只能作为友元函数。
<3>一般将单目运算符和复合运算符(+=,-=,/=,*=,&=,!=,^=,%=,>>=,<<=)重载为成员函数。
<4>一般将双目运算符重载为友元函数。