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

Slides:



Advertisements
Similar presentations
2.8 函数的微分 1 微分的定义 2 微分的几何意义 3 微分公式与微分运算法则 4 微分在近似计算中的应用.
Advertisements

2.6 隐函数微分法 第二章 第二章 二、高阶导数 一、隐式定义的函数 三、可微函数的有理幂. 一、隐函数的导数 若由方程 可确定 y 是 x 的函数, 由 表示的函数, 称为显函数. 例如, 可确定显函数 可确定 y 是 x 的函数, 但此隐函数不能显化. 函数为隐函数. 则称此 隐函数求导方法.
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Tool Command Language --11级ACM班 金天行.
Oracle数据库 Oracle 子程序.
内容提要 代码重用 类的继承 多态 抽象类 多重继承 虚拟继承. 常宝宝 北京大学计算机科学与技术系
第14章 c++中的代码重用.
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
Using C++ The Weird Way Something about c++11 & OOP tricks
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
Object-Oriented Programming in C++ 第四章 运算符重载
4.3函数 4.3.1函数的概念及定义 1、函数的概念: 可以被其它程序调用具有 特定功能的一段相对独立的 程序(模块),称函数。
Hadoop I/O By ShiChaojie.
EBNF 请用扩展的 BNF 描述 C语言里语句的结构; 请用扩展的 BNF 描述 C++语言里类声明的结构;
授课老师:龚涛 信息科学与技术学院 2018年4月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
辅导课程六.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第五讲 四则运算计算器(一) 精品教程《C#程序设计与应用(第2版)清华大学出版社 谭恒松 主编
1.2 MATLAB变量表达式与数据格式 MATLAB变量与表达式 MATLAB的数据显示格式
§2 求导法则 2.1 求导数的四则运算法则 下面分三部分加以证明, 并同时给出相应的推论和例题 .
第一章 函数 函数 — 研究对象—第一章 分析基础 极限 — 研究方法—第二章 连续 — 研究桥梁—第二章.
第二章 Java语言基础.
第十一讲 运算符重载 与类型转换.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
第8章 函 数 一、函数与调用约定 二、函数的总体概念.
第一章 函数与极限.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
1.3 C语言的语句和关键字 一、C语言的语句 与其它高级语言一样,C语言也是利用函数体中的可执行 语句,向计算机系统发出操作命令。按照语句功能或构成的不 同,可将C语言的语句分为五类。 goto, return.
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
$9 泛型基础.
第14讲 运算符重载 运算符重载的概念、方法和规则 运算符重载作为类的成员函数 运算符重载作为类的友元函数 特殊运算符的重载 类型转换函数.
C#面向对象程序设计 $6 深入理解类.
Classes (1) Lecture 6.
第13讲 多态 友员函数 多态性与虚函数 纯虚函数和抽象类.
C语言程序设计 第一章 数据类型, 运算符与表达式 第二章 顺序程序设计 第三章 选择结构程序设计 第四章 循环控制 第五章 数组.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第4章 Excel电子表格制作软件 4.4 函数(一).
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
实验三 16位算术逻辑运算实验 不带进位控制的算术运算 置AR=1: 设置开关CN 1 不带进位 0 带进位运算;
第九节 赋值运算符和赋值表达式.
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
3.16 枚举算法及其程序实现 ——数组的作用.
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Chapter 18 使用GRASP的对象设计示例.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
ASP.NET实用教程 清华大学出版社 第4章 C#编程语言 教学目标 教学重点 教学过程 2019年5月5日.
学习任务三 偏导数 结合一元函数的导数学习二元函数的偏导数是非常有用的. 要求了解二元函数的偏导数的定义, 掌握二元函数偏导数的计算.
第二章 高级函数特性.
辅导课程十五.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第二章 Java基本语法 讲师:复凡.
本节内容 结构体.
程序设计基础A(C语言) 第一章 C语言概述 主讲教师: 许 康
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
复习 标签(label)、文本框(text)控件 按钮:点击事件
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
基本知识 数据类型、变量、常量、运算符.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
第二章 数据类型与表达式 丘志杰 电子科技大学 计算机学院 软件学院.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 this指针 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Presentation transcript:

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

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

原来普通的双参数(非静态的成员函数隐含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*乘号运算符函数和operator/除号运算符函数。

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

[例]普通函数和相应的运算符函数 #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

含对象的表达式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此情形下是错 误的表达式。

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

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

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

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

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

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

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

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

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;}

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))等。

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

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

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

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

[例] 重载加减乘除运算符实现结构变量的四则运算 #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); }

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;

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

例如:对象表达式: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)),这个结果 是临时对象。 指针或算术表达式的结果有两种: 一种结果为右值,对于操作出右值结果的运算符如+-*/ 运算符,在转换为运算符函数时建议返回数值对象。

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

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