计算时间——一个运算符重载示例

运算符重载

运算符重载是一种形式的C++多态。前面介绍过C++是如何使用户能够定义多个名称相同但特征标(参数列表)不同的函数的。这种被称为函数重载或函数多态,旨在让您能够用同名的函数来完成相同的基本操作,即使这种操作被用于不同的数据类型。运算符重载将重载的概念拓展到运算符上,允许赋予C++运算符多种含义。
实际上,很多C++运算符已经被重载。例如,将*运算符用于地址将得到存储在这个地址中的值;但将它用于两个数字时,得到的将是它们的乘积。C++根据操作数的数目和类型来决定采用哪种操作。
将两个数组相加是一种常见的运算。通常需要使用下面的for循环来实现:
for ( int i = 0; i<20; i++)
evening [ i ] = sam [ i ] + janet [ i ]; //add element by element
但在C++中,可以定义一个表示数组的类,并重载 + 运算符。于是:
evening = sam + janet; //add two array objects
这种简单的加法表示法隐藏了内部机理,并强调了实质,这是OOP的另一个目标。

要重载运算符,需使用被称为运算符函数的特殊函数形式。运算符函数的格式如下:
operator op(argument-list)
例如,operator +()重载+运算符,operator ()重载运算符。op必须是有效的C++运算符,不能虚构一个新的符号。例如,不能有operator@()这样的函数,因为C++中没有@运算符。然而,operator 函数将重载[ ]运算符,因为[ ]是数组索引运算符。例如,假设有一个Salesperson类,并为它定义了一个operator+()成员函数,以重载+运算符,以便能够将两个Saleperson对象的销售额相加,则如果district2,sid和sara都是Salesperson类对象,便可以编写这样的等式:
district2 = sid + sara;
编译器发现,操作时Salesperson类对象,因此使用相应的运算符替换上述运算符:
district2 = sid.operator+(sara);
然后该函数将隐式地使用sid(因为它调用了方法),而显式地使用sara对象(因为它被作为参数传递),来计算总和,并返回这个值。

虽然C++对运算符重载做了一些限制,但了解重载的工作方式后,这些限制都很容易理解了。因此,下面首先通过一些示例对运算符重载进行阐述,然后再讨论这些限制。


如果今天早上在Priggs的账户上花费了2小时35分钟,下午又花费了2小时40分钟,则总共花了多少时间呢?这个示例与加法概念很吻合,但要相加的单位(小时与分钟的混合)与内置类型不匹配。
现在采用一个使用方法来处理加法的Time类。首先使用一个名为Sum()的常规方法,然后介绍如何将其转换为重载运算符。

//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);
	Time Sum(const Time& t)const;
	void Show()const;
};
#endif

Time类提供了用于调整和重新设置时间、显示时间、将两个时间相加的方法。程序清单11.2列出了方法定义。请注意,当总的分钟数超过59时,AddMin()和Sum()方法是如何使用整数除法和求模运算符来调整minutes和hours值的。另外,由于这里只使用了iostream的cout,且只使用一次,因此使用std::cout比导入整个名称空间更经济。

//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;
}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之前构造它的拷贝,调用函数将得到该拷贝。

警告:不要返回局部变量或临时变量的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。

最后,程序清单11.3对Time类中计算时间总和的部分进行了测试。

//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;
	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);
	cout << "coding.Sum(fixing) = ";
	total.Show();
	cout << endl;
	return 0;
}

下面是程序清单11.1、程序清单11.2和程序清单11.3组成的程序的输出:
在这里插入图片描述

添加加法运算符

将Time类转换为重载的加法运算符很容易,只要将Sum()的名称改为operator+()即可。这样做是对的,只要把运算符(这里为+)放到operator的后面,并将结果用作方法名即可。在这里,可以在标识符中使用字母、数字或下划线之外的其他字符。程序清单11.4和程序清单11.5反映了这些细微的修改。
程序清单11.4 mytime1.h

//mytime1.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);
	Time operator+(const Time& t)const;
	void Show()const;
};
#endif

程序清单11.5 mytime1.cpp

//mytime0.cpp -- implementing Time methods
#include<iostream>
#include"mytime1.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;
}

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;
}
void Time::Show()const
{
	std::cout << hours << "hours, " << minutes << " minutes";
}

和Sum()一样,operator+()也是由Time对象调用的,它将第二个Time对象作为参数,并返回一个Time对象。因此,可以想调用Sum()那样来调用operator+()方法:
total = coding.operator+(fixing); //function notation
但将该方法命令为operator+()后,也可以使用运算符表示法:
total = coding + fixing; //operator notation
这两种表示法都将调用operator+()方法。
注意,在运算符表示法中,运算符左侧的对象(这里为coding)是调用对象,运算符右边的对象(这里为fixing)是作为参数被传递的对象。
程序清单11.6说明了这点。

//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;

	return 0;
}

下面是程序清单11.4~11.6组成的程序的输出:
在这里插入图片描述
总之,operator+()函数的名称使得可以使用函数表示法或运算符表示法来调用它。编译器将根据操作数的类型来确定如何做:

int a, b, c;
Time A,B,C;
c = a+b; //use int addition
C= A+B; //use addition as defined for Time objects
可以将两个以上的对象相加吗?例如,t1,t2,t3和t4都是Time对象,可以这样做吗:
t4 = t1 + t2 + t3; //valid?
为回答这个问题,来看一些上述语句将被如何转换为函数调用。由于+是从左向右结合的运算符,因此上述语句首先被转换成下面这样:
t4 = t1.operator+(t2+t3); //valid?
然后,函数参数本身被转换为一个函数调用,结果如下:
t4 = t1.operator +(t2.operator +(t3) ); //valid? YES
上述语句合法吗?是的。函数调用t2.operator+(t3)返回一个Time对象,后者是t2和t3的和,然而,该对象成为函数调用t1.operator+()的参数,该调用返回t1与表示t2和t3之和的Time对象的和。总之,最后的返回值为t1,t2和t3之和,这正是我们期望的。

From《C++ Primer Plus》
By——Suki
2020/4/14

发布了29 篇原创文章 · 获赞 27 · 访问量 2952

猜你喜欢

转载自blog.csdn.net/Eumenides_Suki/article/details/105518079