运算符重载
【1】运算符重载的目的:实现类的多态性。
【2】运算符重载的实质:函数重载,每个运算符对应各自的运算符函数,根据操作数的不同调用不同的同名函数。
【3】运算符重载语法:运算符的重载是通过对运算符
运算符函数名由关键字operate和重载的运算符组成:
类型类名::operate 重载的运算符(参数列表)
{
操作://……运算符处理程序代码
}
函数的参数个数是由以下两个因素决定:
-
该操作符是一元操作符还是二元操作符
-
当运算符重载为类的成员函数时,函数的参数个数比原有操作数个数要少一个(后置“++”,“--”除外),也就是说,一元操作符的参数个数为0,二元操作符的参数个数为1;而运算符重载为类的友元函数时,函数的参数个数与原有操作数个数相同(后置“++”,“--”除外),也就是说,一元操作符的参数个数为1,二元操作数的参数个数为2。这是因为,当重载为类的成员函数时,如果某个对象使用重载了成员函数,自身的数据可以直接访问,就不用再放在函数表中进行传递,这样该类本身也作为一个操作符参与了计算。
友元函数对某个对象的数据进行操作,就必须通过该对象的名字来进行,因此参数的使用必须进行传递,操作数的个数也就不会有所减少变化。
【4】运算符重载规则:
-
在C++中,除了5个运算符不能重置(“.”,“*指针”,“::”,“?:”,“sizeof”)之外,其余全部都可以重载。
-
运算符重载可以改变运算符原来的行为,但是重载之后运算符的优先性,结合性和操作数个数都不会改变,只能重载已有的运算符。
-
运算符重载后的功能与原有的功能相似,运算符重载函数的参数至少有一个必须是自定义类型。
-
在重载运算符“()”,“[]”,“->”或“=”时,运算符重载函数必须声明一个为类的一个成员,对于其他运算符,运算符重载函数可以是成员函数或者是友元函数。
【5】成员运算符函数:
eg: classcomplex
{
public:
complex(floatr=0,float i=0); // 构造函数
complexoperator+(const complex &op2) const; //重载运算符 +
complexoperator-(const complex &op2) const; //重载运算符-
complexoperator*(const complex &op2) const; //重载运算符*
voiddisplay() const; // 按数学写法输出复数
private:
floatreal;
floatimag;
};
complex::complex(float r,floati) // 构造函数
{ real=r; imag=i; }
complex complex::operator+(const complex &op2) const //重载运算符 +
{
float r=real+op2.real;
float i=imag+op2.imag;
returncomplex(r,i);
}
complex complex::operator-(const complex &op2) const //重载运算符 -
{
float r=real-op2.real;
float i=imag-op2.imag;
returncomplex(r,i);
}
complex complex:: operator*(const complex &op2) const //重载运算符 *
{
floatr=real*op2.imag-imag*op2.real;
floati=imag*op2.real+real*op2.imag;
returncomplex(r,i);
}
【6】友元运算符函数
-
一般语法形式:
Friend 函数类型 operate 重载的运算符(形参列表)
{
函数体;
}
-
Friend函数不是类的成员,也无this指针,因此重载friend operate函数需要显示地传递操作数,分两种情况:
1:当运算符为双目运算符时。经过重载后,表达式a+b就相当于函数调用operate+(a,b)。
2:当运算符为单目运算符时。经过重载后,表达式—a就相当于函数调用operate--(a);表达式a++重载为类的友元函数,该函数有两个形参,一个是A类对象a,另一个是带有整形的形参,相当于函数调用operate++(a,0)。
Eg: class complex
{
public:
complex(double r=0.0,double i=0.0);
friendcomplex operator+(const complex &c1,const complex &c2);//二元加重载成员函数
friendcomplex operator-(const complex &c1,const complex &c2);//二元减重载成员函数
friendcomplex operator-(const complex &c);//一元减重载成员函数
voidprint() const;//输出复数
private:
doublereal;
doubleimag;
};
complex::complex(doubler,double i)
{
real=r;
imag=i;
}
complex operator+(const complex&c1,const complex &c2)
{
doubler=c1.real+c2.real;
doublei=c1.imag+c2.imag;
returncomplex(r,i);
}
complex operator-(const complex&c1,const complex &c2)
{
doubler=c1.real-c2.real;
doublei=c1.imag-c2.imag;
returncomplex(r,i);
}
complex operator-(const complex &c)
{
returncomplex(-c.real,-c.imag);
}
3:友元运算符函数特点:
1):重载为友元函数时,参数个数=原操作数个数,且至少应该有一个自定义类型的形参。
2):友元函数是类以外的函数,调用这种运算符函数时,所有的操作数都要通过参数传递来获得。
3):friend operate函数不能重载=,(),->运算符。
4)在重载增值和减值运算符时,用friend operate函数需要使用引用参数。
【7】成员运算符函数与友元运算符函数比较
1:对双目运算符而言,成员运算符函数带有一个参数,而友元运算符函数带有两个参数;对于单目运算符而言,成员运算符函数不带参数,而友元运算符带有一个参数。
2:双目运算符一般可以被重载为成员运算符函数和友元运算符函数。但,有一种例外情况,必须使用友元运算符函数。
Eg:classclass::operate+(int x)
{
classtemp;
temp.a=a+x;
temp.b=b+x;
returntemp;
}
ob=100+ob;
当运算符的左操作数是一个整数,而整数是一个内部数据类型,只有当用友元函数来重载运算符函数时,两个参数才能显示地传递给运算符函数,才能解决了运算符“+”左操作数的内部数据类型所带来的问题。
3:若运算符所需的操作数(尤其是第一操作数)需要是隐式类型的转换,那么运算符重载必须用友元函数,而不能用成员函数。
4:一般而言,对于双目运算符,将其重载为一个友元运算符函数比重载为一个成员运算符函数更便于使用。而如果一个运算符的操作需要修改类的对象的状态,那么选择重载为成员函数比较好。
表达式 | 友元函数表示 | 成员函数表示 |
a+b | operator+(a,b) | a.operator(b) |
--a | operator--(a) | a.operator—() |
a++ | operator++(a,0) | a. operator++(0) |
5:运算符的调用形式:
【8】“++”和“--”的重载
1:对于前置方式,如“++a”,若重载为成员函数,则为a.operator( );
若重载为友元函数,则为operator++(a).
2:对于后置方式,则将其看做二元操作符,增加一个int型参数.
如“a--”若重载为成员函数,则为a.operator—(int);
若重载为友元函数,则为operator—(a,int).
3:调用时int不参与运算,只是用来区分重载函数。也就是说,函数内部不需要访问这个参数,因此没有必要为其指定名字,一般传递值为0;
【9】赋值运算符“=”的重载
赋值运算符只能通过一个非静态成员函数来重载。
complex &complex ::operator=(constcomplex &c)//赋值运算符重载函数实现
{
If(this==&c) return *this;
this->real=c.real;
this->imag=c.imag;
return *this;
}
在赋值语言中,赋值运算符左侧是变量,是可以被改变的,而赋值运算符右侧是常量或表达式。函数的返回值作为一个对象的值,被认为是一个常量,因此,函数的返回值是不能出现在赋值运算符的左侧,由于引用的实质是对象的地址,因此,通过引用就能改变对象的值。