Presentation is loading. Please wait.

Presentation is loading. Please wait.

第17章 运算符重载 一、运算符重载的概念 二、禁止重载的运算符 三、运算符重载的规则 四、单目运算符函数 五、双目运算符函数.

Similar presentations


Presentation on theme: "第17章 运算符重载 一、运算符重载的概念 二、禁止重载的运算符 三、运算符重载的规则 四、单目运算符函数 五、双目运算符函数."— Presentation transcript:

1 第17章 运算符重载 一、运算符重载的概念 二、禁止重载的运算符 三、运算符重载的规则 四、单目运算符函数 五、双目运算符函数

2 一、运算符重载的概念 系统的运算符主要分两大种类:一是单目运算符,另一 是双目运算符。用不属于字符集的@代表各种许可的运算
符,运算符构成的表达式抽象的表现格式为: 1. @x x和y是运算符关联的操作数,其原先可以出现的数据 类型是算术类型以及相关的指针类型如char*,CType*等。仅 当存在运算符函数,操作数x或y才可以是对象 。 能转换为运算符函数的普通函数或单参数的函数,此时 称单目运算符函数;或双参数的函数,此时称双目运算符函数.

3 原来普通的双参数(非静态的成员函数隐含this参数)函数:
int CType::Add (int k) {return n+k; } int Sub (CType a,int k) { return a.n-k;} 可以相应地改写为运算符函数(其中n是CType类的int 型数据成员): int CType::operator+ (int k) { return n+k; } int operator- (CType a,int k) { return a.n-k; } 关键字operator是实现普通函数转换为运算符函数的语 是operator+加号运算符函数、operator-减号运算符函数、 operator*乘号运算符函数和operator/除号运算符函数。

4 运算符重载是简化对象运算的函数调用现象,通过定义
@x隐含调用的替代形式。 根据名称细分的结果在函数调用点能够进行唯一的匹配。 例如:iostream类中operator<<左移运算符函数就存在 多个版本。 是全局函数。基于解除私有封装的考虑,全局函数声明为类 的友员函数。 如果类只有公共成员,则无需声明为友员函数。作为非 虚的成员运算符函数和全局运算符函数的重载在编译阶段完 成函数调用的确定,virtual关键字可以修饰作为成员的运算 符函数。

5 [例]普通函数和相应的运算符函数 #include<stdio.h> struct CType { int n; CType(int r=1) { n=r; } int operator+ (int k) { return n+k; } int Add(int k) { return n+k; } }; int operator- (int k,CType a) { return k-a.n; } int Sub (int k,CType a) { return k-a.n; } void main () { CType a (0), b (-7); printf ("%d,%d,%d;", a+1, a.operator+ (1), a.Add (1)); printf ("%d,%d,%d\n",1-b, operator- (1,b), Sub (1,b)); } //输出:1,1,1;8,8,8

6 含对象的表达式a+1等价于显式调用a.operator+(1),
a+1是CType::operator+(int)的隐含调用,隐含调用方便了 对象的操作。 类似地隐含调用1-b等价于operator-(1,b)的显式调用。 1+a不同于a+1,1+a要求匹配operator+(int,CType)型 的全局运算符函数,而b-1要求匹配operator-(CType,int)型 的全局函数或CType::operator-(int)型的成员函数。 由于上例题未提供相应的函数,1+a和b-1此情形下是错 误的表达式。

7 二、禁止重载的运算符 们是:  .  .*  ::  sizeof  ?:
只有区区五个运算符不可以赋予运算符的函数实现,它 们是:   .*  ::  sizeof  ?: 圆点访问成员运算符”.”, 对象访问成员指针运算符”.*”, 这两个运算符分别是箭头访问成员运算符->和对象指针访问 成员指针运算符->*的翻版为对象访问成员保留一条安全的 入口。 作用域分别符::和sizeof运算符都是编译阶段发挥作用。 sizeof运算符的入口参数本身可以是各种类型名。 另一个是三目条件运算符?:,这个运算符本身是if~else 嵌套结构的合理重载。

8 三、运算符重载的规则 存在两种运算符函数的调用格式,一种是将运算符函数 当作函数名为operator@的显式调用格式,另一种是隐含调
用格式,最终编译器在内部转换为显式调用格式。 @x是常规 隐含调用 相应的单目运算符函数;若不存在相应的运算符函数,隐含 调用导致错误。 对于一个特定的类,系统提供等号运算符函数 operator= 和取地址运算符函数operator&供程序调用,即 对于该类的对象x,y可以进行运算:x=y,&x。

9 对于对象表达式的前台隐含操作, 必须存在一个无歧义
的运算符函数作为背景支持。如果无相应的运算符函数或类 型转换函数,则有关对象的隐含调用导致错误。 例如: x+=y是毫无根基的运算,除非存在相关的 operator+= 运算符函数。语句[CType* p;]定义的指针p不 是对象而是一个常规的指针,对于变量指针合适的运算符也 可作用于对象之指针。 运算符始终遵循内部类型所规定的优先原则、结合性。 @x,优先级高的运算符函数优先被编译器隐含调用,同等 级别的运算符函数根据结合性进行分解处理。

10 非静态的成员运算符函数的第一个参量对应隐含this的
当前类的类型。 类型转换运算符、 函数调用运算符()、数组下标索引运 算符 [ ]、箭头运算符-> 函数和等号运算符函数只作为非静 态的成员函数,其余的运算符可以和operator紧贴在一起构 成全局函数。 等号运算符函数operator = 不为派生类继承。 不能凭空捏造C++语言中子虚乌有的运算符如 FORTRAN语句的乘幂运算符**。 运算符函数不允许用户提交缺省的默认值,函数调用运 算符operator()例外。

11 双目或单目全局运算符函数形参类型至少存在一个用户
声明的类型,枚举类只能有全局运算符函数。 例如对于类声明[struct CType {};],CType或CType& 是用户声明的类型。 CType* 类型为常规的指针类型,不用 于构成运算符函数的对象类型。 void operator*(CType&,int){} 是正确的函数定义,而 void operator-(CType*,int){}与void operator+(char,int){} 是错误的。 可以在声明的类上对运算符函数提交任意的语义实现。 但出于与内置类型表达式的接口考虑,最好按照运算符固有 含义进行运算符函数定义。

12 四、单目运算符函数 单目运算符中存在格外的运算符,这就是成为 C++由来的后置运算符。这个运算符从后面作用于
操作数,其运算符重载有其特殊的格式。 对于语句[CType obj;]定义的对象obj,单目运

13 1. 单目成员运算符函数声明时不带参量,编译器隐含补充
this参量。声明格式为: ret_type 返回类型 单目运算符函数名(); 在类外定义的格式为: ret_type () {语句序列;} 非静态的单目成员运算符函数隐含的形参this在调用点 由&obj赋予具体的值。

14 2. 单目全局运算符函数仅带一个用户声明的类型,声明
格式为: ret_type (CType& r); 在实现文件的定义格式为: ret_type ( CType& r) {显含形参r的语句序列;} 全局运算符函数形参r在调用点由obj赋予具体的值,若需要 访问CType类的私有成员则声明为该类的友元函数。 提交一种版本,或者是成员版本,或者是全局版本。

15 int CType::operator! ( ){ printf ("%d!,",m++); return !n;}
[例] 重载逻辑非运算符!负号运算符-和前置运算符++ static int m=1; #include<stdio.h> class CType { int n; friend CType& operator++(CType& q); friend void main(); friend CType operator-(const CType& r); public: CType(int r) { n=r; } int operator!(); }; int CType::operator! ( ){ printf ("%d!,",m++); return !n;}

16 CType operator- (const CType& r)
{ printf ("%d-,", m++); return CType (-r.n); } CType& operator++ (CType& q) { printf ("%d++,", m++); ++q.n; return q; } void main() { CType a (2),b(-1); a=!-++b; printf ("a=%d\n", a.n); a= (operator- (operator++ (b))).operator! ( ); printf ("a=%d", a.n); } //a=1先调用构造函数CType (int)即 a=1相当于a=CType(1) ++b等价于operator++(b), -++b等价于operator-(operator++(b))等。

17 双目运算符函数如果是非静态的成员函数则声明的时候
五、双目运算符函数 双目运算符函数如果是非静态的成员函数则声明的时候 仅带一个参量,编译器隐含的补充一个 this参量。 如果声明为全局函数,则双目运算符函数带两个参量, 其中一个参量的类型必须是一个用户声明的类型。

18 1. 双目成员运算符函数 作为成员的双目运算符函数的声明格式为: ret_type (type); 返回类型 双目运算符函数名 (数据类型); 在类外定义的格式为: ret_type type arg) { 语句序列; } 对于定义语句[CType obj;]设定的对象obj和type型表 达式var,隐含调用的格式为: 调用形式。 非静态的双目成员运算符函数隐含的形参this在 调用点由&obj赋予具体的值,实参var负责形参arg 的初始 化。

19 2. 双目全局运算符函数 双目全局运算符函数由于接口的需要花样增多一倍,相 应的声明格式为:  ret_type (CType& r,type arg); //此格式可以从成员版本变换而来 返回类型 双目运算符函数名 (当前类名,其它类名);  ret_type ( type arg, CType& r); 返回类型 双目运算符函数名 (其它类名, 当前类名); 相应的隐含调用的格式为:

20 obj@var转换为显式调用operator@(obj,var) 即启动
( CType& r,type 式调用 (var,obj) 即启动 type arg, CType& r) 函数,形参r由对象名obj初始化,实参var赋予形参 arg 具 体的初值。 (var)也可 (obj, var)。 两者本质上应是等价的,因此酌情提交一个版本,以免 导致歧义。

21 [例] 重载加减乘除运算符实现结构变量的四则运算
#include<stdio.h> struct CA { CA operator+ ( CA b); CA (int r=1){ n=r; }; int n; }; static int m=1; CA CA::operator+( CA a) {printf ("%d+, ",m++); return CA (n+a.n); } CA operator- (CA a, const CA& r) { printf ("%d-,",m++); return CA (a.n-r.n); } CA operator*(CA a, CA b) {printf ("%d*,",m++); return CA (a.n*b.n); } CA operator/ (const CA& r, const CA& q) { printf ("%d/,",m++); return CA (r.n/q.n); }

22 void main () { CA a,b(2),c(6); a=(a+b)*(c/b)-b; printf ("a=%d; ",a.n); a=operator- ((operator*(a.operator+ (b), operator/ (c, b))),b); } //输出:1/,2+,3*,4-, a=7; 5/,6+,7*,8-,a=25;

23 对于对象的引用返回,引用是已经建立的对象的别名,
返回的是不独立的对象,此时编译器不需要额外建立临时对 象。 对于对象的数值返回,返回一个局部或临时的独立对 象,输送给主控程序。 主控程序会尽早释放临时对象占有的内存。 算术和指针类型的数值返回其函数调用为右值表达式, 对象类型的数值返回其函数调用可以为左值,但由于返回的 临时对象的生存期不由程序员控制,因此返回数值对象的函 数调用不宜作为左值。 返回引用的函数调用则可稳健地作为左值参入运算。

24 例如:对象表达式:a=(a+b)*(c/b)-b;
在临时对象q,t中。接着求q*t的值,结果存放在临时对象s 中,再求s-b的值并把结果存入对象a中。 独立的对象操作自身内存单元,附属的引用操作相关对 象的内存单元。 例如:对于前面单目运算例题,隐含调用++b等价于 operator++(b),该operator++函数是引用形参r,引用r操 作b对象的内存单元。 而- ++b等价于operator-(operator++(b)),这个结果 是临时对象。 指针或算术表达式的结果有两种: 一种结果为右值,对于操作出右值结果的运算符如+-*/ 运算符,在转换为运算符函数时建议返回数值对象。

25 另一种结果为左值,如赋值运算符和复合赋值运算符
+=,-=,*=,/=等。对于这些运算符函数建议处理为对象的引 用返回。 入口形参优先采用引用类型而不是对象的数值类型,内 置类型的形参可以是数值形参或引用形参。 数值形参具有信息的单向安全性,对于对象的引用形参 常加上const修饰以模拟数值形参的单向作用,返回时也可 加const限制。 对于运算符的返回类型最好根据运算符的结果进行返 回,即取地址运算符&返回地址表达式,逻辑非运算符!返回 bool值或int数,前置运算符++返回入口类型的引用等。

26 请打开“第17章(2).ppt”


Download ppt "第17章 运算符重载 一、运算符重载的概念 二、禁止重载的运算符 三、运算符重载的规则 四、单目运算符函数 五、双目运算符函数."

Similar presentations


Ads by Google