运算符重载
运算符重载是一种形式的c++多态。运算符重载将重载的概念扩展到运算符上,允许c++运算符有多重含义。实际上,在c++中已经有运算符被重载,例如*运算符用于地址,将得到存储在这个地址上的值,当将他用于俩个数组时,得到就是它们的乘积。
c++允许将运算符重载扩展到用户定义的类型,例如,允许使用+将两个对象相加,编译器将根据操作数的数目和类型决定使用那种加法定义。重载运算符可以使代码看上去更加自然.
例如,一般将两个数组相加,在前面的做法是使用for循环,代码如下:
for (int i = 0; i < 20; i++)
evening[i] = sam[i] + janet[i];
现在我们可以定义一个表示数组的类,并重载+运算符。就可以像下面这样直接将数组相加
evening = sam + janet;
注释:上面这行代码我们省略了具体实现细节, 主要是明确这个也是OOP的其中一个目标。
要重载运算符,需要使用运算符函数,这种函数格式较为特殊,格式如下:
operatorop(argument-list)
格式就是operator+op+(),其中op就是要重载的运算符,op必须是有效的c++运算符,不能虚构一个新的符号,例如operate@()就是错的,因为c++中没有@运算符。又如,operate[ ]()函数将重载[ ]运算符,其中[ ]运算符是数组索引运算符。
运算符重载实例:
示例:计算时间(将两个时间按分钟和小时相加,重载+运算符)
首先,头文件如下:
// mytime0.h -- Time class before operator overloading
#ifndef MYTIME0_H_
#define MYTIME0_H_
class Time
{
private:
int hours;
int minutes;
public:
Time(); //默认构造函数
Time(int h, int m = 0); //构造函数
void AddMin(int m);
void AddHr(int h); //使用整数除法和求模运算符来调整小时和分钟值
void Reset(int h = 0, int m = 0); //重置时间
const Time Sum(const Time & t) const; ///将时间相加
void Show() const;
};
#endif
实现细节:
// mytime0.cpp -- implementing Time methods
#include <iostream>
#include "mytime0.h"
Time::Time()
{
hours = minutes = 0;
}
Time::Time(int h, int m )
{
hours = h;
minutes = m;
}
void Time::AddMin(int m)
{
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHr(int h)
{
hours += h;
}
void Time::Reset(int h, int m)
{
hours = h;
minutes = m;
}
const Time Time::Sum(const Time & t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
void Time::Show() const
{
std::cout << hours << " hours, " << minutes << " minutes";
}
重点来看下Sum()函数的代码,其中参数是引用,但是返回值不是引用,将参数声明为引用的目的是为了提高效率,此时若使用安置传递Time对象也可,但是传递引用,速度更快,使用的内存更少。
但是这里的返回值不能是引用,因为函数将创建一个新的Time对象(也就是sum)来表示另外两个Time对象的和。返回对象将创建文件的副本,而调用函数可以使用它。然而,如果返回类型时引用(Time &),则引用的将是sum对象。但是sum对象是局部变量,在函数结束时将被删除,因此引用将指向一个不存在的对象。使用返回类型Time意味着程序将在删除sum之前构造它的拷贝,调用函数将得到该拷贝。
警告:不要返回指向局部变量或者临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用指向不存在的数据。
对上面定义的类测试
// usetime0.cpp -- using the first draft of the Time class
// compile usetime0.cpp and mytime0.cpp together
#include <iostream>
#include "mytime0.h"
int main()
{
using std::cout;
using std::endl;
Time planning; //使用默认构造函数创建对象planning
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << endl;
cout << "coding time = ";
coding.Show();
cout << endl;
cout << "fixing time = ";
fixing.Show();
cout << endl;
total = coding.Sum(fixing); //调用函数的对象是coding,显式地调用了fixing
cout << "coding.Sum(fixing) = ";
total.Show();
cout << endl;
// std::cin.get();
return 0;
}
输出结果是:
planning time = 0 hours, 0 minutes
coding time = 2 hours, 40 minutes
fixing time = 5 hours, 55 minutes
coding.Sum(fixing) = 8 hours, 35 minutes
改进上面的类,添加加法运算符
首先头文件:
// mytime1.h -- Time class before operator overloading
#ifndef MYTIME1_H_
#define MYTIME1_H_
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time operator+(const Time & t) const;
void Show() const;
};
#endif
将sum()函数的名称改成operator+()就可以重载加法运算符。此时函数名就是operator+,也是由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;
}
和sum()一样,operator+()也是由Time对象调用的,它将第二个Time对象作为参数,并返回一个Time对象。所以,可以像调用Sum()那样来调用operator+()方法。就像下面这样:
total = coding.operator+(fixing);
重载+运算符之后也可以像是使用运算表示法:
total = coding + fixing;
usetime1.cpp
// usetime1.cpp -- using the second draft of the Time class
// compile usetime1.cpp and mytime1.cpp together
#include <iostream>
#include "mytime1.h"
int main()
{
using std::cout;
using std::endl;
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << endl;
cout << "coding time = ";
coding.Show();
cout << endl;
cout << "fixing time = ";
fixing.Show();
cout << endl;
total = coding + fixing;
// operator notation
cout << "coding + fixing = ";
total.Show();
cout << endl;
Time morefixing(3, 28);
cout << "more fixing time = ";
morefixing.Show();
cout << endl;
total = morefixing.operator+(total);
// function notation
cout << "morefixing.operator+(total) = ";
total.Show();
cout << endl;
// std::cin.get();
return 0;
}
程序输出结果如下:
planning time = 0 hours, 0 minutes
coding time = 2 hours, 40 minutes
fixing time = 5 hours, 55 minutes
coding + fixing = 8 hours, 35 minutes
more fixing time = 3 hours, 28 minutes
morefixing.operator+(total) = 12 hours, 3 minutes
现在,operator+()函数可以使得可以使用函数表示法或者运算符来调用它,编译器将根据操作数的类型来确定+的含义,例如:t1、t2、t3和t4都是Time类型的对象,就可以想下面这样直接想加:
t4 = t1 + t2 + t3;
上面的语句的含义是:由于+是从左到右的运算符,所以语句首先会被转换成下面这样:
t4 = t1.operator+(t2 + t3);
进而转化成下面这样:
t4 = t1.operator+(t1.operator+(t3));
重载限制:
运算符可以重载的条件是
(1)必须有一个操作数是用户定义的类型。目的是为了防止用户为标准类型重载运算符。例如,你不能将减法运算符重载为计算两个double值的和。
(2)使用运算符不能违反运算符原来的句法规则。例如不可以将求模运算符(%)重载成使用一个操作数;
(3)不能修改运算符的优先级。
(4)不能创建新运算符,例如operator**()函数来表示求幂。
(5)不能重载以下的运算符:
下面表中的所有的运算符都可以被重载:
表11.1中的大部分运=运算符都可以通过成员函数或者非成员函数进行重载。
但下面的所列出的符号只能通过成员函数进行重载:
其他重载运算符
在上面的类中重载-和重载*:
(1)首先将相应的函数原型添加到头文件中:
Time operator-(const Time & t) const;
Time operator*(const Time & t) const;
在相应的实现文件中添加:
Time Time::operator-(const Time & t) const
{
Time diff;
int tot1, tot2;
tot1 = t.minutes + 60 * t.hours;
tot2 = minutes + 60 * hours;
diff.minutes = (tot2 - tot1) % 60;
diff.hours = (tot2 - tot1) / 60;
return diff;
}
Time Time::operator*(double mult) const
{
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}