首先说句,为什么要使用操作符重载呢?
其实我们使用别人编写好的类时往往不用考虑,你不用查看cout << a; 中<<是如何重载的,就可以自如地在屏幕上输出或在文件中写入字符,字符串,数字,乃至指针地址,可当我们自己设计类给自己或其他程序员使用时就不得不考虑了。
现在假设我设计一个时间类,初始声明代码如下
class Time
{
private:
int hours;
int minutes;
public:
Time(int h = 0, int m = 0);
};
这里就只有两个成员变量和一个构造函数,实际重载时会有以下几种情况:
1、二元操作符的两个参数均为Time类对象:
这时我们有两种选择,第一种就是常见的成员函数实现:
Time Time::operator +(const Time &t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
第二种就是使用友元函数来实现:
Time operator+(const Time &t1, const Time &t2)
{
Time sum;
sum.minutes = t1.minutes + t2.minutes;
sum.hours = t1.hours + t2.hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
当然,我们要在类声明中添加对应的函数声明,修改类声明代码:
class Time
{
private:
int hours;
int minutes;
public:
Time(int h = 0, int m = 0);
Time operator+ (const Time & t)const;
//friend Time operator+ (const Time & t1, const Time & t2);
};
这里需要注意的就是这两种不能同时定义,否则编译时会有二义性错误,因为当你在其它文件中使用加法时:
Time coding(2,40);
Time fixing(5,55);
Time total;
total = coding + fixing;
编译器将不知道要匹配哪种方法。那么就有人问了,为啥还要有友元函数这种方法呢?是闲着没事做吗?当然不是,我们来看第二种情况。
2、二元操作符的一个参数为Time类对象:
现在呢,其他人给我提供的时间对象是一个double型的变量,单位呢是小时,我需要整合运算,理论上来说只需要重载操作符重载函数即可,也就是加入声明代码:
Time operator+ (double n)const;
以及对应的实现:
Time Time::operator+(double n) const
{
Time result;
int i = n;
n = n - i;
result.minutes = minutes + n * 60;
result.hours = hours + i + result.minutes /60;
result.minutes %= 60;
return result;
}
到这里,假设有个Time类的实例A,计算A+1.5是没问题的,可计算1.5+A是不行的,因为左边的操作数不是调用对象,这里就需要非成员函数,而且还要能访问类的私有成员,于是友元函数就产生了,它在类中声明,但不是成员函数,但又能访问私有成员。所以还需要在类中声明:
friend Time operator+ (double n, const Time & t);
实现文件中定义:
Time operator+ (double n, const Time & t)
{
Time result;
int i = n;
n = n - i;
result.minutes = t.minutes + n * 60;
result.hours = t.hours + i + result.minutes /60;
result.minutes %= 60;
return result;
}
当然有更简单的方法:
Time operator+ (double n, const Time & t)
{
return t * n;
}
这里友元函数和成员函数都需要定义,因为参数顺序不同。
成员函数对应A+1.5,友元函数对应1.5+A。
3、综合使用:
综上,要实现Time类两两相加或加上一个double类型,要2个成员函数和1个友元函数,那有没有简单点的实现呢?
有的,那就是利用类的隐式转换。首先看:
int a = 3.33;
这是可行的,C++将double型的自动转换为int型,假如两种类型互相兼容的话,C++都会自动转换。
那么,当我们在类声明中加入接受一个参数的构造函数时,这个构造函数可以作为转换函数来转换类型。加入声明:
Time (double dhrs);
实现代码:
Time::Time(double dhrs)
{
int i = dhrs;
dhrs= dhrs - i;
hours = i;
minutes = dhrs * 60;
}
那么下面的代码是合法的:
Time planning;
planning = 1.5;
程序将使用构造函数Time( double ) 来创建一个临时Time对象,参数即为1.5。随后,采用逐成员赋值的方法将该临时对象的内容复制到planning中。
notes:
当然,这种特性是可以关闭的,当你要防止意外的自动转换时,可以用关键字explicit来关闭该特性,也就是,声明为:
explicit Time (double dhrs);
显式转换则不受影响。使用转换函数还可以将Time对象隐式转换为double型,关于转换函数更详细地可以再单独了解学习。
现在再回过头来看加法的实现,首先将之前的3个函数去掉,在类中声明如下的友元函数:
friend Time operator+ (const Time & t1, const Time & t2);
在实现文件中添加实现代码:
Time operator+ (const Time & t1, const Time & t2)
{
Time sum;
sum.minutes = t1.minutes + t2.minutes;
sum.hours = t1.hours + t2.hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
这时候就可以实现:
Time A, B;
A + B;
A + 1.5;
1.5 + A;
因为他们被解释为operator+(A, B); operator+(A, 1.5); operator+(1.5, A);
在这些函数中参数的double类型被自动隐式转换为Time类,然后运算。
好了,关于操作符重载就说到这里了,完成,如有错误,还望不吝赐教。