C++运算符重载
所谓运算符重载,就是相同的一个符号,我们可以自定义它的功能。即:和函数的重载类似,相同的名字,不同的功能。
例:实现两个复数的加法,没学习运算符重载之前,我们的做法就是写一个函数来实现两个复数的加法,在进行运算的时候,调用这个函数即可。但是在学了运算符重载之后,我们就可以直接重载加法运算符,使他能够实现两个复数的加法,运算时直接使用加法符号即可,这是不是更简洁直观一点。
运算符重载的语法:
函数类型 operator 运算符(形参)
{
……
}
例:实现一个时间类的加法运算符的重载:
Time Time::operator+(const Time & t) const//+运算符重载
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours+sum.minutes/60;
sum.minutes = sum.minutes % 60;
return sum;
}
我们可以看到,它和普通的函数差不多,就是使用了operator关键字来定义,需要注意的是,这个函数的函数名是【operator+】,函数名由两部分组成,operator和重载的运算符。
实例如下:
#include<iostream>
using namespace std;
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m);
void Addmin(int m);
void Addhr(int h);
void reset(int h = 0, int m = 0);
Time operator+(const Time& t) const;
void show();
friend Time operator*(double a, const Time& t);
friend ostream & operator<<(ostream &os, const Time & t);
};
Time::Time()
{
hours = 0;
minutes = 0;
}
Time::Time(int h, int m)
{
hours = h;
minutes = m;
}
void Time::Addhr(int h)
{
hours = hours + h;
}
void Time::Addmin(int m)
{
minutes = minutes + m;
}
void Time::reset(int h,int m)
{
hours = h;
minutes = m;
}
void Time::show()
{
cout << hours << ":" << minutes << endl;
}
Time Time::operator+(const Time & t) const//+运算符重载
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours+sum.minutes/60;
sum.minutes = sum.minutes % 60;
return sum;
}
Time operator*(double a, const Time & t)
{
Time result;
long tatalminutes = t.hours*a * 60 + t.minutes*a;
result.hours = tatalminutes / 60;
result.minutes = tatalminutes % 60;
return result;
}
ostream & operator<<(ostream & os, const Time & t)
{
os <<"重载:"<< t.hours << ":" << t.minutes << endl;
return os;
}
int main()
{
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time tatal;
cout << "planning=";
planning.show();
cout << endl;
cout << "coding=";
coding.show();
cout << endl;
cout << "fixing=";
fixing.show();
cout << endl;
//tatal = coding.operator+(fixing);
tatal = coding + fixing;
cout << "tatal=";
cout << tatal << endl;
return 0;
}
在上面代码中,这个
Time Time::operator+(const Time & t) const
函数返回的是Time对象,不是他的引用,为什么呢?
因为在这个函数中,我们创建了一个新的Time类对象“sum”,用来保存两个值相加的结果,可以看到它是一个局部变量,它在函数执行完成以后,就会消亡,如果返回它的引用,那将返回一个空的引用,这不是我们想要的。
所以我们可以返回一个Time类的对象,这就意味着在函数执行结束之前,程序会创建sum的拷贝,然后将这个拷贝的值返回给调用者。
警告:不要返回指向局部变量或临时对象的引用。因为在函数执行完毕时,这些局部变量和临时对象将会消亡,引用将指向不存在的数据。
重载运算符的调用有两种方式:
-
类似普通函数的调用,参数是要进行运算的第二个操作数。
tatal = coding.operator+(fixing);
-
类似普通运算符的使用。
tatal = coding + fixing;
重载的限制:
- 重载后的运算符必须至少有一个是用户自己定义的类型。
- 不能违背运算符原本的句法规则
- 不能创建新的运算符
友元函数:
通过让函数成为类的友元,赋予该函数与类的成员函数相同的访问权限。
-
声明:在类体中使用
friend
关键字声明。如:
friend Time operator*(double a, const Time& t);
。 -
定义:在类体或类外定义。在类外定义时,不可再使用
friend
关键字。就和普通函数的定义类似。
重载运算符
<<
:
<<
我们希望可以直接使用
<<
来输出一个时间,而不是通过调用函数来实现。
这个运算符的左边不是我们自定义的类,所以就不能定义为类的成员函数,就要使用非成员函数类实现重载。但是,普通的非成员函数肯定不行,因为我们要在这个函数体中操作Time类中已被声明为private的变量,所以,我们就应该使用类的友元函数,通过友元赋予非成员函数与成员函数相同的访问权限。
即:
friend ostream & operator<<(ostream &os, const Time & t);
我们可以看到,它返回的是一个ostream类的一个引用,因为我们可能要使用连续的
<<
,因为
<<
要求左边的操作数是一个ostream类的对象,所以,可以使用返回引用来解决这个问题。
重载为成员函数和非成员函数
-
成员函数和非成员函数的选择:
(1)重载为成员函数:
-
如果操作的第一个数是我们自定义的对象,就可以声明为类的成员函数。
例:如果要重载B为类成员函数,使之能实现表达式 oprd1 B oprd2 ,其中,oprd1 为A类对象,则B应该被重载为A类的成员函数,形参类型应该是 oprd2 所属类型。
(2)重载为非成员函数:
- 运算符的左操作数不是对象,就不能重载为成员函数。就需要将其重载为类外的非成员函数。
- 左操作数是类的对象,但这个类不是我们自己创建的。就必须重载为非成员函数。
-
如果操作的第一个数是我们自定义的对象,就可以声明为类的成员函数。
-
运算符重载为非成员函数的规则:
(1)函数的形参代表依次自左到右次序排列的个操作数。
(2)重载为非成员函数时:
- 参数个数 = 原操作数(后置++ ,- – 除外)
- 至少应该有一个自定义类型的参数。
(3)后置单目运算符++ 和- – 的重载函数,形参列表要增加一个int,但不必写形参名。
(4)如果在运算符的重载函数中需要操作某个对象的私有成员,可以将该函数声明为该类的友元。
一般来说,非成员函数应是友元函数,这样它才能直接访问类的私有成员。
非成员函数的重载运算符的函数所需的参数与运算符使用的操作数数目相同;
成员函数的重载运算符的函数所需的参数比运算符使用的操作数少一个,少的那个操作数通过this指针隐式的传递。