今天继续复习了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 <stdio.h>
#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 <stdio.h>
#include <stdlib.h>
#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.不能重载以下运算符:
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)
{
<span style="white-space:pre"> </span>os << t.hours << " hours, " << t.minutes << "minutes";
return os;
}
这样重载以后就可以实现诸如下面的语句:
Time A(1, 20);
cout << A << "12312" << endl;
因为cout<<A 过后返回了一个ostream对象,然后这个ostream对象后面接着又是<<,所以可以这样连续地输出。
所以我们可以总结一下:
1、如果要将基本运算符应用到类实例之间、类与基本类型之间的计算,可以使用运算符重载。
2、如果要重载一个运算符,用于计算两个不同类型的实例,比如上面的一个Time类实例和一个double类型的实数,可以使用友元函数,然后在友元函数中改变计算顺序,调用成员函数。
3、如果要直接用 cout<< 来输出类,可以利用友元重载 << 运算符。这里的第一个参数和返回值是一个ostream类实例,另外,还可以使用ofstream,可以把类直接输出到文件中:
#include <fstream>
...
ofstream fout;
fout.open("savetime.txt");
Time A(1,2);
fout << A;
...
如有错误,请批评指正