C程序设计谭浩强.ppt

上传人:max****ui 文档编号:8617582 上传时间:2020-03-30 格式:PPT 页数:78 大小:240.05KB
返回 下载 相关 举报
C程序设计谭浩强.ppt_第1页
第1页 / 共78页
C程序设计谭浩强.ppt_第2页
第2页 / 共78页
C程序设计谭浩强.ppt_第3页
第3页 / 共78页
点击查看更多>>
资源描述
第10章运算符重载 10 1什么是运算符重载10 2运算符重载的方法10 3重载运算符的规则10 4运算符重载函数作为类成员函数和友元函数10 5重载双目运算符10 6重载单目运算符10 7重载流插入运算符和流提取运算符10 8不同类型数据间的转换 所谓重载 就是重新赋予新的含义 函数重载就是对一个已有的函数赋予新的含义 使之实现新功能 运算符也可以重载 实际上 我们已经在不知不觉之中使用了运算符重载 现在要讨论的问题是 用户能否根据自己的需要对C 已提供的运算符进行重载 赋予它们新的含义 使之一名多用 譬如 能否用 号进行两个复数的相加 在C 中不能在程序中直接用运算符 对复数进行相加运算 用户必须自己设法实现复数相加 例如用户可以通过定义一个专门的函数来实现复数相加 见例10 1 10 1什么是运算符重载 例10 1通过函数来实现复数相加 includeusingnamespacestd classComplex 定义Complex类 public Complex real 0 imag 0 定义构造函数Complex doubler doublei real r imag i 构造函数重载Complexcomplex add Complex c imag imag c2 imag returnc voidComplex display 定义输出函数 cout real imag i endl intmain Complexc1 3 4 c2 5 10 c3 定义3个复数对象c3 plex add c2 调用复数相加函数cout c1 c1 display 输出c1的值cout c2 c2 display 输出c2的值cout c1 c2 c3 display 输出c3的值return0 运行结果如下 c1 3 4i c2 5 10i c1 c2 8 6i 结果无疑是正确的 但调用方式不直观 太烦琐 使人感到很不方便 能否也和整数的加法运算一样 直接用加号 来实现复数运算呢 如c3 c1 c2 编译系统就会自动完成c1和c2两个复数相加的运算 如果能做到 就为对象的运算提供了很大的方便 这就需要对运算符 进行重载 运算符重载的方法是定义一个重载运算符的函数 在需要执行被重载的运算符时 系统就自动调用该函数 以实现相应的运算 也就是说 运算符重载是通过定义函数实现的 运算符重载实质上是函数的重载 重载运算符的函数一般格式如下 函数类型operator运算符名称 形参表列 对运算符的重载处理 例如 想将 用于Complex类 复数 的加法运算 函数的原型可以是这样的 Complexoperator Complex 10 2运算符重载的方法 在定义了重载运算符的函数后 可以说 函数operator 重载了运算符 为了说明在运算符重载后 执行表达式就是调用函数的过程 可以把两个整数相加也想像为调用下面的函数 intoperator inta intb return a b 如果有表达式5 8 就调用此函数 将5和8作为调用函数时的实参 函数的返回值为13 这就是用函数的方法理解运算符 可以在例10 1程序的基础上重载运算符 使之用于复数相加 例10 2改写例10 1 重载运算符 使之能用于两个复数相加 includeusingnamespacestd classComplex public Complex real 0 imag 0 Complex doubler doublei real r imag i Complexoperator Complex returnc voidComplex display cout real imag i endl intmain Complexc1 3 4 c2 5 10 c3 c3 c1 c2 运算符 用于复数运算cout c1 c1 display cout c2 c2 display cout c1 c2 c3 display return0 运行结果与例10 1相同 c1 3 4i c2 5 10i c1 c2 8 6i 请比较例10 1和例10 2 只有两处不同 1 在例10 2中以operator 函数取代了例10 1中的complex add函数 而且只是函数名不同 函数体和函数返回值的类型都是相同的 2 在main函数中 以 c3 c1 c2 取代了例10 1中的 c3 plex add c2 在将运算符 重载为类的成员函数后 C 编译系统将程序中的表达式c1 c2解释为c1 operator c2 其中c1和c2是Complex类的对象即以c2为实参调用c1的运算符重载函数operator Complex c2 进行求值 得到两个复数之和 虽然重载运算符所实现的功能完全可以用函数实现 但是使用运算符重载能使用户程序易于编写 阅读和维护 在实际工作中 类的声明和类的使用往往是分离的 假如在声明Complex类时 对运算符 都进行了重载 那么使用这个类的用户在编程时可以完全不考虑函数是怎么实现的 放心大胆地直接使用 进行复数的运算即可 十分方便 对上面的运算符重载函数operator 还可以改写得更简练一些 ComplexComplex operator Complex 需要说明的是 运算符被重载后 其原有的功能仍然保留 没有丧失或改变 通过运算符重载 扩大了C 已有运算符的作用范围 使之能用于类对象 运算符重载对C 有重要的意义 把运算符重载和类结合起来 可以在C 程序中定义出很有实用意义而使用方便的新的数据类型 运算符重载使C 具有更强大的功能 更好的可扩充性和适应性 这是C 最吸引人的特点之一 1 C 不允许用户自己定义新的运算符 只能对已有的C 运算符进行重载 2 C 允许重载的运算符C 中绝大部分的运算符允许重载 具体规定见书中表10 1 不能重载的运算符只有5个 成员访问运算符 成员指针访问运算符 域运算符 sizeof 长度运算符 条件运算符 10 3重载运算符的规则 前两个运算符不能重载是为了保证访问成员的功能不能被改变 域运算符和sizeof运算符的运算对象是类型而不是变量或一般表达式 不具重载的特征 3 重载不能改变运算符运算对象 即操作数 的个数 4 重载不能改变运算符的优先级别 5 重载不能改变运算符的结合性 6 重载运算符的函数不能有默认的参数 否则就改变了运算符参数的个数 与前面第 3 点矛盾 7 重载的运算符必须和用户定义的自定义类型的对象一起使用 其参数至少应有一个是类对象 或类对象的引用 也就是说 参数不能全部是C 的标准类型 以防止用户修改用于标准类型数据的运算符的性质 8 用于类对象的运算符一般必须重载 但有两个例外 运算符 和 不必用户重载 赋值运算符 可以用于每一个类对象 可以利用它在同类对象之间相互赋值 地址运算符 也不必重载 它能返回类对象在内存中的起始地址 9 应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能 10 运算符重载函数可以是类的成员函数 如例10 2 也可以是类的友元函数 还可以是既非类的成员函数也不是友元函数的普通函数 在本章例10 2程序中对运算符 进行了重载 使之能用于两个复数的相加 在该例中运算符重载函数operator 作为Complex类中的成员函数 是双目运算符 为什么在例10 2程序中的重载函数中只有一个参数呢 实际上 运算符重载函数有两个参数 由于重载函数是Complex类中的成员函数 有一个参数是隐含的 运算符函数是用this指针隐式地访问类对象的成员 10 4运算符重载函数作为类成员函数和友元函数 可以看到 重载函数operator 访问了两个对象中的成员 一个是this指针指向的对象中的成员 一个是形参对象中的成员 如this real c2 real this real就是c1 real 在10 2节中已说明 在将运算符函数重载为成员函数后 如果出现含该运算符的表达式 如c1 c2 编译系统把它解释为c1 operator c2 即通过对象c1调用运算符重载函数 并以表达式中第二个参数 运算符右侧的类对象c2 作为函数实参 运算符重载函数的返回值是Complex类型 返回值是复数c1和c2之和 Complex c1 real c2 real c1 imag c2 imag 运算符重载函数除了可以作为类的成员函数外 还可以是非成员函数 可以将例10 2改写为例10 3 例10 3将运算符 重载为适用于复数加法 重载函数不作为成员函数 而放在类外 作为Complex类的友元函数 includeusingnamespacestd classComplex public Complex real 0 imag 0 Complex doubler doublei real r imag i friendComplexoperator Complex Complexoperator Complex 与例10 2相比较 只作了一处改动 将运算符函数不作为成员函数 而把它放在类外 在Complex类中声明它为友元函数 同时将运算符函数改为有两个参数 在将运算符 重载为非成员函数后 C 编译系统将程序中的表达式c1 c2解释为operator c1 c2 即执行c1 c2相当于调用以下函数 Complexoperator Complex 求出两个复数之和 运行结果同例10 2 为什么把运算符函数作为友元函数呢 因为运算符函数要访问Complex类对象中的成员 如果运算符函数不是Complex类的友元函数 而是一个普通的函数 它是没有权利访问Complex类的私有成员的 在10 2节中曾提到过 运算符重载函数可以是类的成员函数 也可以是类的友元函数 还可以是既非类的成员函数也不是友元函数的普通函数 现在分别讨论这3种情况 首先 只有在极少的情况下才使用既不是类的成员函数也不是友元函数的普通函数 原因是上面提到的 普通函数不能直接访问类的私有成员 在剩下的两种方式中 什么时候应该用成员函数方式 什么时候应该用友元函数方式 二者有何区别呢 如果将运算符重载函数作为成员函数 它可以通过this指针自由地访问本类的数据成员 因此可以少写一个函数的参数 但必须要求运算表达式第一个参数 即运算符左侧的操作数 是一个类对象 而且与运算符函数的类型相同 因为必须通过类的对象去调用该类的成员函数 而且只有运算符重载函数返回值与该对象同类型 运算结果才有意义 在例10 2中 表达式c1 c2中第一个参数c1是Complex类对象 运算符函数返回值的类型也是Complex 这是正确的 如果c1不是Complex类 它就无法通过隐式this指针访问Complex类的成员了 如果函数返回值不是Complex类复数 显然这种运算是没有实际意义的 如想将一个复数和一个整数相加 如c1 i 可以将运算符重载函数作为成员函数 如下面的形式 ComplexComplex operator int 注意在表达式中重载的运算符 左侧应为Complex类的对象 如c3 c2 i 不能写成c3 i c2 运算符 的左侧不是类对象 编译出错如果出于某种考虑 要求在使用重载运算符时运算符左侧的操作数是整型量 如表达式i c2 运算符左侧的操作数i是整数 这时是无法利用前面定义的重载运算符的 因为无法调用i operator 函数 可想而知 如果运算符左侧的操作数属于C 标准类型 如int 或是一个其他类的对象 则运算符重载函数不能作为成员函数 只能作为非成员函数 如果函数需要访问类的私有成员 则必须声明为友元函数 可以在Complex类中声明 friendComplexoperator int 错误 类型不匹配 请注意 数学上的交换律在此不适用 如果希望适用交换律 则应再重载一次运算符 如Complexoperator Complex 这样 使用表达式i c2和c2 i都合法 编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数 可以将以上两个运算符重载函数都作为友元函数 也可以将一个运算符重载函数 运算符左侧为对象名的 作为成员函数 另一个 运算符左侧不是对象名的 作为友元函数 但不可能将两个都作为成员函数 原因是显然的 C 规定 有的运算符 如赋值运算符 下标运算符 函数调用运算符 必须定义为类的成员函数 有的运算符则不能定义为类的成员函数 如流插入 类型转换运算符 由于友元的使用会破坏类的封装 因此从原则上说 要尽量将运算符函数作为成员函数 但考虑到各方面的因素 一般将单目运算符重载为成员函数 将双目运算符重载为友元函数 在学习了本章第10 7节例10 9的讨论后 读者对此会有更深入的认识 说明 有的C 编译系统 如VisualC 6 0 没有完全实现C 标准 它所提供不带后缀 h的头文件不支持把成员函数重载为友元函数 上面例10 3程序在GCC中能正常运行 而在VisualC 6 0中会编译出错 但是VisualC 所提供的老形式的带后缀 h的头文件可以支持此项功能 因此可以将程序头两行修改如下 即可顺利运行 include以后如遇到类似情况 亦可照此办理 双目运算符 或称二元运算符 是C 中最常用的运算符 双目运算符有两个操作数 通常在运算符的左右两侧 如3 5 a b i 10等 在重载双目运算符时 不言而喻在函数中应该有两个参数 下面再举一个例子说明重载双目运算符的应用 10 5重载双目运算符 例10 4定义一个字符串类String 用来存放不定长的字符串 重载运算符 用于两个字符串的等于 小于和大于的比较运算 为了使读者便于理解程序 同时也使读者了解建立程序的步骤 下面分几步来介绍编程过程 1 先建立一个String类 includeusingnamespacestd classString public String p NULL 默认构造函数String char str 构造函数voiddisplay private char p 字符型指针 用于指向字符串 String String char str 定义构造函数 p str 使p指向实参字符串voidString display 输出p所指向的字符串 cout p intmain Stringstring1 Hello string2 Book string1 display cout endl string2 display return0 运行结果为HelloBook 2 有了这个基础后 再增加其他必要的内容 现在增加对运算符重载的部分 为便于编写和调试 先重载一个运算符 程序如下 include includeusingnamespacestd classString public String p NULL String char str friendbooloperator String voidString display 输出p所指向的字符串 cout String 程序运行结果为1 这只是一个并不很完善的程序 但是 已经完成了实质性的工作了 运算符重载成功了 其他两个运算符的重载如法炮制即可 3 扩展到对3个运算符重载 在String类体中声明3个成员函数 friendbooloperator Stringelse returnfalse booloperator String 运行结果为100结果显然是对的 到此为止 主要任务基本完成 4 再进一步修饰完善 使输出结果更直观 下面给出最后的程序 includeusingnamespacestd classString public String p NULL String char str friendbooloperator String voiddisplay private char p String String char str p str voidString display 输出p所指向的字符串 cout String booloperator String string1 String string2 if strcmp string1 p string2 p string1 string2 1 string1 display cout string2 display elseif operator string1 string2 1 string1 display coutBookBook ComputerHello Hello 增加了一个compare函数 用来对两个字符串进行比较 并输出相应的信息 这样可以减轻主函数的负担 使主函数简明易读 通过这个例子 不仅可以学习到有关双目运算符重载的知识 而且还可以学习怎样去编写C 程序 这种方法的指导思想是 先搭框架 逐步扩充 由简到繁 最后完善 边编程 边调试 边扩充 千万不要企图在一开始时就解决所有的细节 类是可扩充的 可以一步一步地扩充它的功能 最好直接在计算机上写程序 每一步都要上机调试 调试通过了前面一步再做下一步 步步为营 这样编程和调试的效率是比较高的 读者可以试验一下 单目运算符只有一个操作数 如 a b c p 还有最常用的 i和 i等 重载单目运算符的方法与重载双目运算符的方法是类似的 但由于单目运算符只有一个操作数 因此运算符重载函数只有一个参数 如果运算符重载函数作为成员函数 则还可省略此参数 下面以自增运算符 为例 介绍单目运算符的重载 10 6重载单目运算符 例10 5有一个Time类 包含数据成员minute 分 和sec 秒 模拟秒表 每次走一秒 满60秒进一分钟 此时秒又从0开始算 要求输出分和秒的值 includeusingnamespacestd classTime public Time minute 0 sec 0 默认构造函数Time intm ints minute m sec s 构造函数重载Timeoperator 声明运算符重载函数voiddisplay cout 60 sec 60 满60秒进1分钟 minute return this 返回当前对象值 intmain Timetime1 34 0 for inti 0 i 61 i time1 time1 display return0 运行情况如下 34 134 2 34 5935 035 1 共输出61行 可以看到 在程序中对运算符 进行了重载 使它能用于Time类对象 和 运算符有两种使用方式 前置自增运算符和后置自增运算符 它们的作用是不一样的 在重载时怎样区别这二者呢 针对 和 这一特点 C 约定 在自增 自减 运算符重载函数中 增加一个int型形参 就是后置自增 自减 运算符函数 例10 6在例10 5程序的基础上增加对后置自增运算符的重载 修改后的程序如下 includeusingnamespacestd classTime public Time minute 0 sec 0 Time intm ints minute m sec s Timeoperator 声明前置自增运算符 重载函数Timeoperator int 声明后置自增运算符 重载函数voiddisplay cout 60 sec 60 minute return this 返回自加后的当前对象 TimeTime operator int 定义后置自增运算符 重载函数 Timetemp this sec if sec 60 sec 60 minute returntemp 返回的是自加前的对象 intmain Timetime1 34 59 time2 cout time1 time1 display time1 cout time1 time1 display time2 time1 将自加前的对象的值赋给time2cout time1 time1 display cout time2 time2 display 输出time2对象的值 请注意前置自增运算符 和后置自增运算符 二者作用的区别 前者是先自加 返回的是修改后的对象本身 后者返回的是自加前的对象 然后对象自加 请仔细分析后置自增运算符重载函数 运行结果如下 time1 34 59 time1原值 time1 35 0 执行 time1后time1的值 time1 35 1 再执行time1 后time1的值 time2 35 0 time2保存的是执行time1 前time1的值 可以看到 重载后置自增运算符时 多了一个int型的参数 增加这个参数只是为了与前置自增运算符重载函数有所区别 此外没有任何作用 编译系统在遇到重载后置自增运算符时 会自动调用此函数 C 的流插入运算符 是C 在类库中提供的 所有C 编译系统都在类库中提供输入流类istream和输出流类ostream cin和cout分别是istream类和ostream类的对象 在类库提供的头文件中已经对 进行了重载 使之作为流插入运算符和流提取运算符 能用来输出和输入C 标准类型的数据 因此 在本书前面几章中 凡是用 cout 对标准类型数据进行输入输出的 都要用 include把头文件包含到本程序文件中 10 7重载流插入运算符和流提取运算符 用户自己定义的类型的数据 是不能直接用 来输出和输入的 如果想用它们输出和输入自己声明的类型的数据 必须对它们重载 对 重载的函数形式如下 istream即重载运算符 的函数的第一个参数和函数的类型都必须是istream 类型 第二个参数是要进行输入操作的类 重载 和 的函数作为友元函数或普通的函数 而不能将它们定义为成员函数 在程序中 人们希望能用插入运算符 usingnamespacestd classComplex public Complex real 0 imag 0 Complex doubler doublei real r imag i Complexoperator Complex 运算符 重载为友元函数private 10 7 1重载流插入运算符 doublereal doubleimag ComplexComplex operator Complex 在VisualC 6 0环境下运行时 需将第一行改为 include 并删去第2行 运行结果为 8 14i 可以看到在对运算符 重载后 在程序中用 不仅能输出标准类型数据 而且可以输出用户自己定义的类对象 用 cout c3 即能以复数形式输出复数对象c3的值 形式直观 可读性好 易于使用 下面对怎样实现运算符重载作一些说明 程序中重载了运算符 运算符重载函数中的形参output是ostream类对象的引用 形参名output是用户任意起的 分析main函数最后第二行 cout c3 运算符 的左面是cout 前面已提到cout是ostream类对象 的右面是c3 它是Complex类对象 由于已将运算符 的重载函数声明为Complex类的友元函数 编译系统把 cout c3 解释为operator cout c3 即以cout和c3作为实参 调用下面的operator 函数 ostream 请注意 上一行中的 是C 预定义的流插入符 因为它右侧的操作数是字符串常量和double类型数据 执行cout语句输出复数形式的信息 然后执行return语句 请思考 returnoutput的作用是什么 回答是能连续向输出流插入信息 output是ostream类的对象 它是实参cout的引用 也就是cout通过传送地址给output 使它们二者共享同一段存储单元 或者说output是cout的别名 因此 returnoutput就是returncout 将输出流cout的现状返回 即保留输出流的现状 请问返回到哪里 刚才是在执行cout c3 在已知cout c3的返回值是cout的当前值 如果有以下输出 cout c3 c2 先处理cout c3 即 cout c3 c2 而执行 cout c3 得到的结果就是具有新内容的流对象cout 因此 cout c3 c2相当于cout 新值 c2 运算符 左侧是ostream类对象cout 右侧是Complex类对象c2 则再次调用运算符 重载函数 接着向输出流插入c2的数据 现在可以理解了为什么C 规定运算符 重载函数的第一个参数和函数的类型都必须是ostream类型的引用 就是为了返回cout的当前值以便连续输出 请读者注意区分什么情况下的 是标准类型数据的流插入符 什么情况下的 是重载的流插入符 如 cout c3 5 endl 有下划线的是调用重载的流插入符 后面两个 不是重载的流插入符 因为它的右侧不是Complex类对象而是标准类型的数据 是用预定义的流插入符处理的 还有一点要说明 在本程序中 在Complex类中定义了运算符 重载函数为友元函数 因此只有在输出Complex类对象时才能使用重载的运算符 对其他类型的对象是无效的 如cout time1 time1是Time类对象 不能使用用于Complex类的重载运算符 C 预定义的运算符 的作用是从一个输入流中提取数据 如 cin i 表示从输入流中提取一个整数赋给变量i 假设已定义i为int型 重载流提取运算符的目的是希望将 用于输入自定义类型的对象的信息 10 7 2重载流提取运算符 例10 8在例10 7的基础上 增加重载流提取运算符 用 cin 输入复数 用 coutusingnamespacestd classComplex public friendostream istream operator istream input Complex c 定义重载运算符 cout c real c imag returninput intmain Complexc1 c2 cin c1 c2 cout c1 c1 endl cout c2 c2 endl return0 运行情况如下 inputrealpartandimaginarypartofcomplexnumber 36 inputrealpartandimaginarypartofcomplexnumber 410 c1 3 6i c2 4 10i 以上运行结果无疑是正确的 但并不完善 在输入复数的虚部为正值时 输出的结果是没有问题的 但是虚部如果是负数 就不理想 请观察输出结果 inputrealpartandimaginarypartofcomplexnumber 36 inputrealpartandimaginarypartofcomplexnumber 4 10 c1 3 6i c2 4 10i 根据先调试通过 最后完善的原则 可对程序作必要的修改 将重载运算符 0 output 虚部为正数时 在虚部前加 号output c imag i endl 虚部为负数时 在虚部前不加 号returnoutput 这样 运行时输出的最后一行为c2 4 10i 通过本章前面几节的讨论 可以看到 在C 中 运算符重载是很重要的 很有实用意义的 它使类的设计更加丰富多彩 扩大了类的功能和使用范围 使程序易于理解 易于对对象进行操作 它体现了为用户着想 方便用户使用的思想 有了运算符重载 在声明了类之后 人们就可以像使用标准类型一样来使用自己声明的类 类的声明往往是一劳永逸的 有了好的类 用户在程序中就不必定义许多成员函数去完成某些运算和输入输出的功能 使主函数更加简单易读 好的运算符重载能体现面向对象程序设计思想 在本章的例子中读者应当注意到 在运算符重载中使用引用 reference 的重要性 利用引用作为函数的形参可以在调用函数的过程中不是用传递值的方式进行虚实结合 而是通过传址方式使形参成为实参的别名 因此不生成临时变量 实参的副本 减少了时间和空间的开销 此外 如果重载函数的返回值是对象的引用时 返回的不是常量 而是引用所代表的对象 它可以出现在赋值号的左侧而成为左值 leftvalue 可以被赋值或参与其他操作 如保留cout流的当前值以便能连续使用 输出 但使用引用时要特别小心 因为修改了引用就等于修改了它所代表的对象 在C 中 某些不同类型数据之间可以自动转换 例如inti 6 i 7 5 i 编译系统对7 5是作为double型数处理的 在求解表达式时 先将6转换成double型 然后与7 5相加 得到和为13 5 在向整型变量i赋值时 将13 5转换为整数13 然后赋给i 这种转换是由C 编译系统自动完成的 用户不需干预 这种转换称为隐式类型转换 10 8不同类型数据间的转换10 8 1标准类型数据间的转换 C 还提供显式类型转换 程序人员在程序中指定将一种指定的数据转换成另一指定的类型 其形式为类型名 数据 如int 89 5 其作用是将89 5转换为整型数89 对于用户自己声明的类型 编译系统并不知道怎样进行转换 解决这个问题的关键是让编译系统知道怎样去进行这些转换 需要定义专门的函数来处理 转换构造函数 conversionconstructorfunction 的作用是将一个其他类型的数据转换成一个类的对象 先回顾一下以前学习过的几种构造函数 默认构造函数 以Complex类为例 函数原型的形式为Complex 没有参数用于初始化的构造函数 函数原型的形式为Complex doubler doublei 形参表列中一般有两个以上参数用于复制对象的复制构造函数 函数原型的形式为 10 8 2转换构造函数 Complex Complex 其作用是将double型的参数r转换成Complex类的对象 将r作为复数的实部 虚部为0 用户可以根据需要定义转换构造函数 在函数体中告诉编译系统怎样去进行转换 在类体中 可以有转换构造函数 也可以没有转换构造函数 视需要而定 以上几种构造函数可以同时出现在同一个类中 它们是构造函数的重载 编译系统会根据建立对象时给出的实参的个数与类型选择形参与之匹配的构造函数 使用转换构造函数将一个指定的数据转换为类对象的方法如下 1 先声明一个类 2 在这个类中定义一个只有一个参数的构造函数 参数的类型是需要转换的类型 在函数体中指定转换的方法 3 在该类的作用域内可以用以下形式进行类型转换 类名 指定类型的数据 就可以将指定类型的数据转换为此类的对象 不仅可以将一个标准类型数据转换成类对象 也可以将另一个类的对象转换成转换构造函数所在的类对象 如可以将一个学生类对象转换为教师类对象 可以在Teacher类中写出下面的转换构造函数 Teacher Student 但应注意 对象s中的num name sex必须是公用成员 否则不能被类外引用 用转换构造函数可以将一个指定类型的数据转换为类的对象 但是不能反过来将一个类的对象转换为一个其他类型的数据 例如将一个Complex类对象转换成double类型数据 C 提供类型转换函数 typeconversionfunction 来解决这个问题 类型转换函数的作用是将一个类的对象转换成另一类型的数据 如果已声明了一个Complex类 可以在Complex类中这样定义类型转换函数 operatordouble returnreal 类型转换函数的一般形式为 10 8 3类型转换函数 operator类型名 实现转换的语句 在函数名前面不能指定函数类型 函数没有参数 其返回值的类型是由函数名中指定的类型名来确定的 类型转换函数只能作为成员函数 因为转换的主体是本类的对象 不能作为友元函数或普通函数 从函数形式可以看到 它与运算符重载函数相似 都是用关键字operator开头 只是被重载的是类型名 double类型经过重载后 除了原有的含义外 还获得新的含义 将一个Complex类对象转换为double类型数据 并指定了转换方法 这样 编译系统不仅能识别原有的double型数据 而且还会把Complex类对象作为double型数据处理 那么程序中的Complex类对具有双重身份 既是Complex类对象 又可作为double类型数据 Complex类对象只有在需要时才进行转换 要根据表达式的上下文来决定 转换构造函数和类型转换运算符有一个共同的功能 当需要的时候 编译系统会自动调用这些函数 建立一个无名的临时对象 或临时变量 例10 9使用类型转换函数的简单例子 includeusingnamespacestd classComplex public Complex real 0 imag 0 Complex doubler doublei real r imag i operatordouble returnreal 类型转换函数private doublereal doubleimag intmain Complexc1 3 4 c2 5 10 c3 doubled d 2 5 c1 要求将一个double数据与Complex类数据相加cout d endl return0 分析 1 如果在Complex类中没有定义类型转换函数operatordouble 程序编译将出错 2 如果在main函数中加一个语句 c3 c2 由于赋值号两侧都是同一类的数据 是可以合法进行赋值的 没有必要把c2转换为double型数据 3 如果在Complex类中声明了重载运算符 函数作为友元函数 Complexoperator Complexc1 Complexc2 定义运算符 重载函数 returnComplex c1 real c2 real c1 imag c2 imag 若在main函数中有语句 c3 c1 c2 由于已对运算符 重载 使之能用于两个Complex类对象的相加 因此将c1和c2按Complex类对象处理 相加后赋值给同类对象c3 如果改为d c1 c2 d为double型变量将c1与c2两个类对象相加 得到一个临时的Complex类对象 由于它不能赋值给double型变量 而又有对double的重载函数 于是调用此函数 把临时类对象转换为double数据 然后赋给d 从前面的介绍可知 对类型的重载和本章开头所介绍的对运算符的重载的概念和方法都是相似的 重载函数都使用关键字operator 因此 通常把类型转换函数也称为类型转换运算符函数 由于它也是重载函数 因此也称为类型转换运算符重载函数 或称强制类型转换运算符重载函数 假如程序中需要对一个Complex类对象和一个double型变量进行 等算术运算 以及关系运算和逻辑运算 如果不用类型转换函数 就要对多种运算符进行重载 以便能进行各种运算 这样 是十分麻烦的 工作量较大 程序显得冗长 如果用类型转换函数对double进行重载 使Complex类对象转换为double型数据 就不必对各种运算符进行重载 因为Complex类对象可以被自动地转换为double型数据 而标准类型的数据的运算 是可以使用系统提供的各种运算符的 例10 10包含转换构造函数 运算符重载函数和类型转换函数的程序 先阅读以下程序 在这个程序中只包含转换构造函数和运算符重载函数 includeusingnamespacestd classComplex public Complex real 0 imag 0 默认构造函数Complex doubler real r imag 0 转换构造函数Complex doubler doublei real r imag i 实现初始化的构造函数friendComplexoperator Complexc1 Complexc2 重载运算符 的友元函数voiddisplay private doublereal doubleimag Complexoperator Complexc1 Complexc2 定义运算符 重载函数 returnComplex c1 real c2 real c1 imag c2 imag voidComplex display cout real imag i endl intmain Complexc1 3 4 c2 5 10 c3 c3 c1 2 5 复数与double数据相加c3 display return0 对程序的分析 1 如果没有定义转换构造函数 则此程序编译出错 2 现在 在类Complex中定义了转换构造函数 并具体规定了怎样构成一个复数 由于已重载了算符 在处理表达式c1 2 5时 编译系统把它解释为 operator c1 2 5 由于2 5不是Complex类对象 系统先调用转换构造函数Complex 2 5 建立一个临时的Complex类对象 其值为 2 5 0i 上面的函数调用相当于operator c1 Complex 2 5 将c1与 2 5 0i 相加 赋给c3 运行结果为 5 5 4i 3 如果把 c3 c1 2 5 改为c3 2 5 c1 程序可以通过编译和正常运行 过程与前相同 从中得到一个重要结论 在已定义了相应的转换构造函数情况下 将运算符 函数重载为友元函数 在进行两个复数相加时 可以用交换律 如果运算符函数重载为成员函数 它的第一个参数必须是本类的对象 当第一个操作数不是类对象时 不能将运算符函数重载为成员函数 如果将运算符 函数重载为类的成员函数 交换律不适用 由于这个原因 一般情况下将双目运算符函数重载为友元函数 单目运算符则多重载为成员函数 4 如果一定要将运算符函数重载为成员函数 而第一个操作数又不是类对象时 只有一个办法能够解决 再重载一个运算符 函数 其第一个参数为double型 当然此函数只能是友元函数 函数原型为friendoperator double Complex显然这样做不太方便 还是将双目运算符函数重载为友元函数方便些 5 在上面程序的基础上增加类型转换函数 operatordouble returnreal 此时Complex类的公用部分为public Complex real 0 imag 0 Complex doubler real r imag 0 转换构造函数Complex doubler doublei real r imag i operatordouble returnreal 类型转换函数friendComplexoperator Complexc1 Complexc2 重载运算符 voiddisplay 其余部分不变 程序在编译时出错 原因是出现二义性
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!