第 18讲 运算符重载
教学目的与要求:
了解运算符重载的作用 。
掌握运算符重载的两种形式 。
教学内容提要:
1,运算符重载的概述;
2,运算符重载为友元函数;
3,运算符重载为成员函数;
教学重点:运算符重载的两种形式 。
教学难点:运算符重载的两种形式 。
教学进度,P164~P182
教学过程:
18.1运算符重载概述
18.1.1 什么是运算符重载
1,为什么要运算符重载?
C++中预定义的运算符操作对象只能是基本数据类型 。 实际上,对于很多用户自定义类型 ( 比如类 ),也需要有类似的运算操作 。 这就提出了对运算符进行重新定义,赋予已有符号以新的功能的要求 。
2,运算符重载:是对已有的运算符赋予多重含义,同一个运算符作用于不同类型的数据导致不同类型的行为 。
运算符重载的实质就是函数重载 。 在实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参,然后根据实参的类型来确定需要调用的函数,这个过程是在编译过程中完成的 。
注意:在 C++中,运算符是系统定义的函数。
定义一个简化的复数类 complex:
class complex {
public:
double real,imag;
complex(double r=0,double i=0)
{ real=r; imag=i;}
};
若要把类 complex的两个对象 com1和 com2加在一起,下面的语句是不能实现的,
main()
{
complex com1(1.1,2.2),com2(3.3,4.4),total;
total=com1+com2; //错误
//…
return 0;
}
若要将上述类 complex的两个对象相加,只要编写一个运算符函数 operator+(),如下所示,
complex operator+(complex om1,complex om2)
{
complex temp;
temp.real=om1.real+om2.real;
temp.imag=om1.imag+om2.imag;
return temp;
}
我们就能方便地使用语句,
total=com1+com2;
将类 complex的两个对象 com1和 com2相加。
18.1.2 运算符重载的规则
1,C++中的运算符除了少数几个外,全部可以重载,而且只能重载已有的这些运算符 。 不能被重载的运算符:,*”,,,:,,
,?,,,―sizeof‖,―.‖。
2,重载之后运算符的操作数个数,优先级和结合性不能改变 。
3,用于类的对象的运算符必须重载,但是有两种例外情形:
① 赋值运算符 ( =) 无需重载就可以用于每一个类 。 在不提供重载的赋值运算符时,赋值运算符的默认行为是逐个拷贝类的数据成员 。
② 地址运算符 & 也无需重载就可以用于任何类的对象,
它返回对象在内存中的地址 。 当然地址运算符也可以被重载 。
4,在重载运算符 ( ),[ ],–> 或者 = 时,运算符重载必须声明为类的一个成员 。 对于其它的运算符,运算符重载函数可以是友元 。
5,C++语言中只能重载原先已有定义的运算符 。
18.2运算符重载的两种形式
18.2.1 友元运算符函数
1,友元运算符函数定义的语法形式友元运算符函数的原型在类的内部声明格式如下,
class X {
//…
friend 返回类型 operator 运算符 (形参表 );
//…
}
在类外定义友元运算符函数的格式如下,
返回类型 operator 运算符 (形参表 )
{
函数体
}
说明:参数表的参数个数一个参数 单目运算符二个参数 双目运算符
2,双目运算符重载当用友元函数重载双目运算符时,两个操作数都要传递给运算符函数。
调用形式:
一般而言,如果在类 X中采用友元函数重载双目运算符 @,
而 aa和 bb是类 X的两个对象,则以下两种函数调用方法是等价的,
aa @ bb; // 隐式调用
operator @(aa,bb); // 显式调用例 18.1 复数类加减法运算符重载 —— 友元函数形式
class complex
{ double real,imag;
public:
complex(double r=0,double i=0)
{ real=r; imag=r;}
friend complex operator +( complex c1,complex c2);
friend complex operator -( complex c1,complex c2);
void print();
};
complex operator +(complex c1,complex c2)
{ complex c;
c.real=c1.real+c2.real;
c.imag=c1.imag+c2.imag;
return c;
}
complex operator - (complex c1,complex c2)
{ complex c;
c.real=c1.real-c2.real;
c.imag=c1.imag-c2.imag;
return c;
}
c3=c1+c2 相当于 c3=operator+(c1,c2);
c3=c1-c2 相当于 c3=operator-(c1,c2);
3,单目运算符重载用友元函数重载单目运算符时,需要一个显式的操作数 。
调用形式:
一般而言,如果在类 X中采用友元函数重载单目运算符 @,而 aa是类 X的对象,则以下两种函数调用方法是等价的,
@aa; // 隐式调用
operator@(aa); // 显式调用例 18.2,见教材例 5.4 用友元函数重载单目运算符,-” 。
18.2.2 成员运算符函数在 C++中,可以把运算符函数定义成某个类的成员函数,称为成员运算符函数。
1,成员运算符函数定义的语法形式成员运算符函数的原型在类的内部声明格式如下,
class X {
//…
返回类型 operator运算符 (形参表 );
//…
}
在类外定义成员运算符函数的格式如下,
返回类型 X::operator运算符 (形参表 )
{
函数体
}
说明:参数表的参数个数无参数 单目运算符一个参数 双目运算符
2,双目运算符重载对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过 this指针隐含地传递给函数的。
调用形式:
一般而言,如果在类 X中采用成员函数重载双目运算符 @,
成员运算符函数 operator@ 所需的一个操作数由对象 aa通过
this指针隐含地传递,它的另一个操作数 bb在参数表中显示,aa和 bb是类 X的两个对象,则以下两种函数调用方法是等价的,
aa @ bb; // 隐式调用
aa.operator @(bb); // 显式调用例 18.3:复数类加减法运算符重载 ——成员函数形式
c3=c1+c2 相当于 c3=c1.operator+(c2);
c3=c1-c2 相当于 c3=c1.operator-(c2);
3,单目运算符重载对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数 。
调用形式:
一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的,
@aa; // 隐式调用
aa.operator@(); // 显式调用成员运算符函数 operator @所需的一个操作数由对象
aa通过 this指针隐含地传递 。 因此,在它的参数表中没有参数 。
例 18.4,见教材例 5.8重载单目运算符,++”。
18.2.3 成员运算符函数与友元运算符函数的比较
(1) 对双目运算符而言,成员运算符函数带有一个参数,而友元运算符函数带有两个参数 ;对单目运算符而言,成员运算符函数不带参数,而友元运算符函数带一个参数。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但有一种情况,必须使用友元函数。
(3) 成员运算符函数和友元运算符函数可以用习惯方式调用,
也可以用它们专用的方式调用,表 5.2列出了一般情况下运算符函数的调用形式。
(4) C++的大部分运算符既可说明为成员运算符函数,又可说明为友元运算符函数。究竟选择哪一种运算符好一些,没有定论,这主要取决于实际情况和程序员的习惯。
小结:
1,运算符重载的作用和规则 。
2,运算符重载为友元函数的格式和调用形式
3,运算符重载为成员函数的格式和调用形式作业:
P210