C++运算符重载以及友元

今天继续复习了C++,之前都没认真看过《C++ primer plus》,非常详细的一本书,而且适合查阅与学习。


1.重载

在C++中有很多运算符可以被应用到类与类之间、类与基本类型之间等,我们把这种方法叫做运算符重载,比如有两个类X的实例A和B,将加号(+)在该类中重载以后就能直接使用A+B这样的式子。

下面用《C++ primer plus》中一个重载的例子来说明.

这个例子是一个时间类的例子,可以将两个时间相加,相减,乘以一个常数,运用cout<<将时间输出。

version1(版本1):

mytime0.h:

// 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 operator+(const Time & t) const;  //重载+运算符
	void Show() const;
};

#endif

mytime0.cpp:

// mytime0.cpp -- implementing Time methods
#include 
#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
{
	printf("%2d hours, %2d minutes\n", hours, minutes);
}
usetime0.cpp:

// usetime.cpp -- using the first draft of the Time class
// compile usetime0.cpp and mytime0.cpp together
#include 
#include 
#include "mytime1.h"

int main()
{
	Time planning;
	Time coding(2, 40);
	Time fixing(5, 55);
	Time total;

	printf("planning time = ");
	planning.Show();

	printf("coding time = ");
	coding.Show();

	printf("fixing time = ");
	fixing.Show();

	total = coding + fixing;
	printf("coding + fixing = ");
	total.Show();

	Time morefixing(3, 28);
	printf("more fixing time = ");
	morefixing.Show();

	total = morefixing.operator+(total);
	printf("morefixing.operator+(total) = ");
	total.Show();

	system("pause");
	return 0;
}
将上面三个文件一起编译。

程序中写到了total = coding + fixing; 但是可能有人会有疑问,可以将两个以上的对象相加吗?即total = A+B+C; 合法吗?答案是肯定的。因为加法是从左向右结合的,所以total = A+B+C;等价于下面这个式子:

total = (A+(B+C));

然后等价于:total = A.operator+(B+C);

再进一步:total = A.operator+(B.operator+(C));

所以在计算B.operator+(C)后将返回值作为引用传递给A.operator(const Time & t),所以连续的加法也是可行的。


c++中的重载限制:

1.重载后的运算符必须至少有一个操作数是用户定义的类型,这是为了防止用户为标准类型重载运算符。

2.使用运算符是不能违反运算符原来的语法规则。例如,不能将求模运算符(%)重载成使用一个操作数。重载过后的运算符的优先级不会改变。

3.不能创建新的运算符,比如,不能定义operator**()函数来求幂。

4.不能重载以下运算符:

C++运算符重载以及友元_第1张图片

5.下面的运算符只能在成员函数中重载:


我们还可以重载减号(-)和星号(*):

Time operator-(const Time & t) const;
Time operator*(const double & n) const;
//----上面是原型,下面是定义
Time Time::operator-(const Time & t) const
{
	Time diff;
	int tot1, tot2;
	tot1 = hours * 60 + minutes;
	tot2 = t.hours * 60 + t.minutes;
	diff.hours = (tot1 - tot2) / 60;
	diff.minutes = (tot1 - tot2) % 60;
	return diff;
}

Time Time::operator*(const double & n) const
{
	Time result;
	result.hours = (int)((hours * 60 + minutes) * n) / 60;
	result.minutes = (int)((hours * 60 + minutes) * n) % 60;
	return result;
}

2.友元

上面有一个对 * 号的重载,但是在重载之后,*号的前面只能是Time类实例,后面只能是数字,即:

Time A,B;
A = B * 1.5;  //合法
A = 1.5 * B;  //不合法

要想达到比较自然的乘法运算,单单像上面那样重载是不足的。这就要用到友元函数,友元函数不是一个类的成员函数,但是却拥有和成员函数一样的访问权限,所以我们在定义的时候不需要加上类域解析符。

friend Time operator*(const double n, const Time & t) { return t * n; }
这里有一个技巧,就是在友元函数中调用成员函数,即友元函数计算 1.5*B 时,调用成员函数,让其返回 B*1.5 的值。


可能有人还会想要直接用cout << 输出time类,这也是可以的。利用友元函数重载:

//原型
friend std::ostream & operator<<(std::ostream & os, const Time & t);
//定义
std::ostream & operator<<(std::ostream & os, const Time & t)
{
	os << t.hours << " hours, " << t.minutes << "minutes";
	return os;
}


解释一下上面这个重载,它的返回类型是对ostream类型实例的引用,第一个参数是一个ostream类型实例的引用,第二个参数是Time类实例。

这样重载以后就可以实现诸如下面的语句:

Time A(1, 20);
cout << A << "12312" << endl;

因为cout<


所以我们可以总结一下:

1、如果要将基本运算符应用到类实例之间、类与基本类型之间的计算,可以使用运算符重载。

2、如果要重载一个运算符,用于计算两个不同类型的实例,比如上面的一个Time类实例和一个double类型的实数,可以使用友元函数,然后在友元函数中改变计算顺序,调用成员函数。

3、如果要直接用 cout<< 来输出类,可以利用友元重载 << 运算符。这里的第一个参数和返回值是一个ostream类实例,另外,还可以使用ofstream,可以把类直接输出到文件中:

#include 
...
ofstream fout;
fout.open("savetime.txt");
Time A(1,2);
fout << A;
...


如有错误,请批评指正

你可能感兴趣的:(C/C++)