C++.NET程序设计(中)ppt.ppt_第1页
C++.NET程序设计(中)ppt.ppt_第2页
C++.NET程序设计(中)ppt.ppt_第3页
C++.NET程序设计(中)ppt.ppt_第4页
C++.NET程序设计(中)ppt.ppt_第5页
已阅读5页,还剩368页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C NET程序设计 中 课件设计者 杜茂康2009年9月 第5章继承第6章多态性第7章模板与STL第8章异常 第5章继承 继承使软件复用变得简单 易行 可以通过继承复用已有的程序资源 缩短软件开发的周期 本章主要介绍继承的方式 要注意在继承方式下派生类与基类对象之间的关系 以及派生类构造函数如何提供对基类的构造 5 1继承的概念 1 继承的概念 以存在的类为基础定义新的类 新类即捅有基类的数据成员和成员函数 5 1继承的概念 2 继承目的代码重用coderesue描述能力 类属关系广泛存在IsAvs HasA3 有关概念基类 超类派生类 子类 5 1继承的概念 4 派生类可实施的对基类的改变增加新的数据成员和成员函数 重载基类的成员函数 重定义基类已有的成员函数 改变基类成员在派生类中的访问属性 5 派生类不能继承基类的以下内容基类的构造函数和析构函数 基类的友元函数 静态数据成员和静态成员函数 5 1继承的概念 5 2继承方式 C 的继承类型可分为公有继承 保护继承和私有继承 也称为公有派生 保护派生和私有派生 不同继承方式会不同程度地影响基类成员在派生类中的访问权限 5 2 1继承方式 继承语法形式classB classD private protected public B public最常见的派生方式维持基类成员的可访问性派生类不可直接访问基类的private成员 可通过基类的共有成员函数访问 5 2 2公有继承 例题ch5 1 cpp classbase intx public voidsetx intn x n intgetx returnx voidshowx cout x endl classderived publicbase inty public voidsety intn y n voidsety y getx voidshowy cout y endl Setx Getx Showx x Setx Getx Showx x Sety Gety Showy y 接口 私有数据 base derived voidmain derivedobj obj setx 10 obj sety 20 obj showx obj showy obj sety obj showx obj showy 5 2 2公有继承 5 2 3私有继承 Private基类的中的public成员在派生类中是private private成员在派生类中不可访问 例5 私有继承的例子 includeusingnamespacestd classBase intx public voidsetx intn x n intgetx returnx voidshowx cout x endl classderived privatebase inty public voidsety intn y n voidsety y getx voidshowy cout y endl voidmain derivedobj obj setx 10 cannotaccessobj sety 20 obj showx cannotaccessobj showy Setx Getx Showx x Setx Getx Showx x Sety Gety Showy y 接口 私有数据 base derived 基类中protected的成员类内部 可以访问类的使用者 不能访问类的派生类成员 可以访问 例5 2 保护成员的例子 5 2 4保护成员 classB private inti protected intj public intk classD publicB public voidf i 1 cannotaccessj 2 k 3 voidmain Bb b i 1 cannotaccessb j 2 cannotaccessb k 3 K k i i f 接口 私有数据 B D j j 5 2 5保护继承 派生方式为protected的继承称为保护继承 在这种继承方式下 基类的public成员在派生类中会变成protected成员 基类的protected和private成员在派生类中保持原来的访问权限 例 保护继承的例子 includeusingnamespacestd classBase intx protected intgetx returnx public voidsetx intn x n voidshowx cout x endl classDerived protectedBase inty public voidsety intn y n voidsety y getx 访问基类的保护成员voidshowy cout y endl voidmain Derivedobj obj setx 10 错误obj sety 20 obj showx 错误 obj showy 基类成员在派生类中的访问权限 不能继承的基类内容1 构造函数 析构函数2 友员关系3 针对基类定义的一些特殊运算符 如new等 派生类 5 3基类与派生类的关系 1 成员函数的重定义和名字隐藏派生类对基类成员函数的重定义或重载会影响基类成员函数在派生类中的可见性 基类的同名成员函数会被派生类重载的同名函数所隐藏 例5 3 派生类重载基类成员函数的例子 2 派生类和基类的关系派生类继承了基类的所有成员 尽管有些继承的成员是不可访问的派生类可以 添加新的数据成员和 或函数成员修改继承的函数成员的行为 overloading 覆盖继承的函数成员 overriding classBase basemembers classDerived Base newmembers 派生类对基类成员的访问形式通过派生类对象直接访问基类成员在派生类成员函数中直接访问基类成员通过基类名字限定访问被重载的基类成员名 5 3基类与派生类的关系 5 3基类与派生类的关系 例5 3 在派生类中重载基类成员 CH5 3 cpp includeusingnamespacestd classBase intx public voidsetx inti x i voidset intn x n voidprint cout Baseclass x x endl public voidset intp intk m p n k 重载基类的成员函数set voidset inti intj intk 重载成员函数set Base set i 调用基类成员函数set m j n k voidprint 重定义基类的成员函数print Base print cout DerivedClass m m endl cout DerivedClass n n endl voidmain Derivedd d set 1 3 L1d print L2d set 5 6 7 L3d print d set 10 L4 错误 只能是d Base set 10 d Base print L5d setx 8 L6 5 3 2访问基类成员 例5 4 在派生类中访问基类成员 CH5 4 cpp includeusingnamespacestd classB intx public voidset inti x i voidsetx inti x i voidprintx cout x x endl classD publicB inty public voidsety intp y p voidprinty cout y y endl voidsetxy inti intj setx i L1在派生类成员函数中直接访问基类成员y j voidset inti intj B set i L2访问基类set成员y j voidmain Dobj obj setx 2 L3访问基类成员obj printx L4访问基类成员 输出x 2obj sety 3 L5访问派生类成员obj set 1 2 L6访问派生类set成员obj B set 3 L7访问基类set成员 B和D的示意类图 5 4构造函数和析构函数 补充 类对象成员的构造先构造对象成员再构造自身 调用构造函数 例题ch cpp classA public A cout ConstructingA endl A cout DestructingA endl classB public B cout ConstructingB endl B cout DestructingB endl classC public C cout ConstructingC endl C cout DestructingC endl Bb Aa voidmain Cc ConstructingBConstructingAConstructingCDestructingCDestructingADestructingB 如果 classB publicA classC publicB 结果又当如何 5 4 1派生类构造函数的定义 派生类可能有多个基类 也可能包括多个成员对象 在创建派生类对象时 派生类的构造函数除了要负责本类成员的初始化外 还要调用基类和成员对象的构造函数 并向它们传递参数 以完成基类子对象和成员对象的建立和初始化 派生类只能采用构造函数初始化列表的方式向基类或成员对象的构造函数传递参数 形式如下 派生类构造函数名 参数表 基类构造函数名 参数表 成员对象名1 参数表 5 4 1派生类构造函数的定义 例5 5 派生类Derived以构造函数初始化列表的方式向基类构造函数提供参数 CH5 5 cpp includeusingnamespacestd classBase private intx public Base inta x a cout Baseconstructorx x endl Base cout Basedestructor endl classDerived publicBase private inty public Derived inta intb Base a 派生类构造函数的初始化列表y b cout Derivedconstructory y endl Derived cout Deriveddestructor endl voidmain Derivedd 1 2 派生类对象的构造先构造基类再构造成员最后构造自身 调用构造函数 基类构造顺序由派生层次决定 最远的基类最先构造成员构造顺序和定义顺序符合析构函数的析构顺序与构造相反 5 4 2构造函数和析构函数调用次序 例题ch cpp classA public A cout ConstructingA endl A cout DestructingA endl classB public B cout ConstructingB endl B cout DestructingB endl classC public C cout ConstructingC endl C cout DestructingC endl classD publicC public D cout ConstructingD endl D cout DestructingD endl Bb Aa Cc voidmain Dd ConstructingCConstructingBConstructingAConstructingCConstructingDDestructingDDestructingCDestructingADestructingBDestructingC 5 4 3构造函数和析构函数的构造规则 1 派生类可以不定义构造函数的情况当具有下述情况之一时 派生类可以不定义构造函数 基类没有定义任何构造函数 基类具有缺省参数的构造函数 基类具有无参构造函数 例5 7 没有构造函数的派生类 CH5 7 cpp includeusingnamespacestd classA public A cout ConstructingA endl A cout DestructingA endl classB publicA public B cout DestructingB endl voidmain Bb 5 4 3构造函数和析构函数的构造规则 2 派生类必须定义构造函数的情况当基类或成员对象所属类只含有带参数的构造函数时 即使派生类本身没有数据成员要初始化 它也必须定义构造函数 并以构造函数初始化列表的方式向基类和成员对象的构造函数传递参数 以实现基类子对象和成员对象的初始化 4 4 3构造函数和析构函数的构造规则 例5 8 派生类构造函数的定义 CH5 8 cpp includeusingnamespacestd classPoint protected intx y public Point inta intb 0 x a y b cout constructingpoint x y endl classLine publicPoint protected intlen public Line inta intb intl Point a b 构造函数初始化列表len l cout ConstructingLine len len endl voidmain LineL1 1 2 3 3 派生类的构造函数只负责直接基类的初始化C 语言标准有一条规则 如果派生类的基类同时也是另外一个类的派生类 则每个派生类只负责它的直接基类的构造函数调用 这条规则表明当派生类的直接基类只有带参数的构造函数 但没有默认构造函数时 包括缺省参数和无参构造函数 它必须在构造函数的初始化列表中调用其直接基类的构造函数 并向基类的构造函数传递参数 以实现派生类对象中的基类子对象的初始化 这条规则有一个例外情况 当派生类存在虚基类时 所有虚基类都由最后的派生类负责初始化 5 4 3构造函数和析构函数的构造规则 例5 9 当同时存在直接基类和间接基类时 每个派生类只负责其直接基类的构造 CH5 9 cpp includeusingnamespacestd classA intx public A intaa x aa cout ConstructingA endl A cout DestructingA endl classB publicA public B intx A x cout ConstructingB endl classC publicB public C inty B y cout ConstructingC endl voidmain Cc 1 4 构造函数的调用时间和次序当派生类具有多个基类和多个对象成员 它们的构造函数将在创建派生类对象时被调用 调用次序如下 基类构造函数 对象成员构造函数 派生类构造函数 5 4 3构造函数和析构函数的构造规则 1 当有多个基类时 将按照它们在继承方式中的声明次序调用 与它们在构造函数初始化列表中的次序无关 当基类A本身又是另一个类B的派生类时 则先调用基类B的构造函数 再调用基类A的构造函数 2 当有多个对象成员时 将按它们在派生类中的声明次序调用 与它们在构造函数初始化列表中的次序无关 3 当构造函数初始化列表中的基类和对象成员的构造函数调用完成之后 才执行派生类构造函数体中的程序代码 5 4 3构造函数和析构函数的构造规则 例5 10 构造函数的调用次序验证 CH5 10 cpp includeusingnamespacestd classA intx public A inti 0 x i cout A x endl A cout Des A x endl classB inty public B inti y i cout B y endl B cout Des B y endl classC intz public C inti z i cout C z endl C cout Des C z endl classD publicB public Cc1 c2 Aa0 a4 D a4 4 c2 2 c1 1 B 1 cout D 5 endl D cout Des D 5 endl voidmain Dd 5 5多重继承 5 5 1多继承的概念和应用C 允许一个类从一个或多个基类派生 如果一个类只有一个基类 就称为单一继承 如果一个类具有两个或两个以上的基类 就称为多重继承 多继承的形式如下 class派生类名 继承方式 基类名1 继承方式 基类名2 其中 继承方式可以是public protected private 5 5 1多继承的概念和应用 例5 11 上图的简单程序 CH5 11 cpp includeusingnamespacestd classBase1 private intx protected intgetx returnx public voidsetx inta 1 x a 5 5 1多继承的概念和应用 classBase2 private inty public voidsety inta y a intgety returny classBase3 private intz public voidsetz inta z a intgetz returnz classDerived publicBase1 publicBase2 publicBase3 private intd public voidsetd inta d a voiddisplay voidDerived display cout Base1 x getx endl cout Base2 y gety endl cout Base3 z getz endl cout Derived d d endl voidmain Derivedobj obj setx 1 obj sety 2 obj setz 3 obj setd 4 obj display 5 5 2多继承下的二义性 在多继承方式下 派生类继承了多个基类的成员 当两个不同基类拥有同名成员时 容易产生名字冲突问题 例5 12 类A和类B是MI的基类 它们都有一个成员函数f 在类MI中就有通过继承而来的两个同名成员函数f CH5 12 cpp includeusingnamespacestd classA public voidf cout FromA endl classB public voidf cout FromB endl classMI publicA publicB public voidg cout FromMI endl voidmain MImi mi f 错误mi A f 正确 5 5 3多继承的构造函数与析构函数 派生类必须负责为每个基类的构造函数提供初始化参数 构造的方法和原则与单继承相同 构造函数的调用次序仍然是先基类 再对象成员 然后才是派生类的构造函数 基类构造函数的调用次序与它们在被继承时的声明次序相同 与它们在派生类构造函数的初始化列表中的次序没有关系 多继承方式下的析构函数调用次序仍然与构造函数的调用次序相反 例5 13 类Base1 Base2 Base3 Derived的继承关系如图所示 验证其构造函数和析构函数的调用次序 5 5 3多继承的构造函数与析构函数 5 6 1引入的原因重复基类派生类间接继承同一基类使得间接基类 Person 在派生类中有多份拷贝 引发二义性 5 6继拟继承 虚拟基类在派生类中只存在一份拷贝 解决了基类数据成员的二义性问题 5 6 1引入的原因重复基类 1 虚拟继承virtualinheritance的定义语法classderived class virtual base class虚基类virtualbaseclass被虚拟继承的基类在其所以的派生类中 仅出现一次 5 6 2虚拟继承的实现 classA public voidvf cout IcomefromclassA endl classB publicA classC publicA classD publicB publicC voidmain Dd d vf error A B C D Vf Vf Vf B A Vf C A Vf A Vf 例5 14 类A是类B C的虚基类 类D从类B C继承 在类D中调用基类A的成员会产生二义性 classA public voidvf cout IcomefromclassA endl classB virtualpublicA classC virtualpublicA classD publicB publicC voidmain Dd d vf okay A B C D Vf Vf Vf A Vf 将 例5 14 改为虚拟继承不会产生二义性 2 虚拟继承的构造次序虚基类的初始化与一般的多重继承的初始化在语法上是一样的 但构造函数的调用顺序不同 若基类由虚基类派生而来 则派生类必须提供对间接基类的构造 即在构造函数初始列表中构造虚基类 无论此虚基类是直接还是间接基类 调用顺序的规定 先调用虚基类的构造函数 再调用非虚基类的构造函数若同一层次中包含多个虚基类 这些虚基类的构造函数按它们的说明的次序调用若虚基类由非基类派生而来 则仍然先调用基类构造函数 再调用派生类构造函数 例5 15 虚基类的执行次序分析 CH5 15 cpp includeusingnamespacestd classA inta public A cout ConstructingA endl classB public B cout ConstructingB endl 5 6 2虚拟继承的实现 classB1 virtualpublicB virtualpublicA public B1 inti cout ConstructingB1 endl classB2 publicA virtualpublicB public B2 intj cout ConstructingB2 endl classD publicB1 publicB2 public D intm intn B1 m B2 n cout ConstructingD endl Aa voidmain Dd 1 2 3 虚基类由最终派生类初始化在没有虚拟继承的情况下 每个派生类的构造函数只负责其直接基类的初始化 但在虚拟继承方式下 虚基类则由最终派生类的构造函数负责初始化 在虚拟继承方式下 若最终派生类的构造函数没有明确调用虚基类的构造函数 编译器就会尝试调用虚基类不需要参数的构造函数 包括缺省 无参和缺省参数的构造函数 如果没找到就会产生编译错误 4 6 2虚拟继承的实现 例5 16 类A是类B C的虚基类 类ABC从B C派生 是继承结构中的最终派生类 它负责虚基类A的初始化 CH5 16 cpp includeclassA inta public A intx a x cout VirtualBassA endl classB virtualpublicA public B inti A i cout VirtualBassB endl classC virtualpublicA intx public C inti A i cout ConstructingC endl x i classABC publicC publicB public ABC inti intj intk C i B j A i L1 这里必须对A进行初始化 cout ConstructingABC endl voidmain ABCobj 1 2 3 5 7基类与派生类对象的关系 基类对象与派生类对象之间存在赋值相容性 包括以下几种情况 把派生类对象赋值给基类对象 把派生类对象的地址赋值给基类指针 用派生类对象初始化基类对象的引用 反之则不行 即不能把基类对象赋值给派生类对象 不能把基类对象的地址赋值给派生类对象的指针 也不能把基类对象作为派生对象的引用 例5 17 把派生类对象赋值给基类对象的例子 CH5 17 cpp includeusingnamespacestd classA inta public voidsetA intx a x intgetA returna classB publicA intb public voidsetB intx b x intgetB returnb voidf1 Aa intx a setA x voidf2 A pA intx pA setA x voidf3 A pA setA 20 coutgetA getA endl cout b1 getA endl b1 setA 7 cout b1 getA endl f1 b1 100 cout b1 getA endl f2 5 7基类与派生类对象的关系 说明 不论以哪种方式把派生类对象赋值给基类对象 都只能访问到派生类对象中的基类子对象部份的成员 不能访问派生类的自定义成员 只能把派生类对象赋值给基类对象 不能把基类对象赋值给派生类对象 5 7基类与派生类对象的关系 5 8继承与组合 继承与组合 也称合成 是C 实现代码重用的两种主要方法 通过继承 派生类可以获得基类的程序代码 从而达到代码重用的目的 而组合则体现了类之间的另一种关系 是指一个类可以包容另外的类 即用其他类来定义它的对象成员 继承关系常被称为 Is a 关系 即两个类之间若存在Is a关系 就可以用继承来实现它 比如 水果和梨 水果和苹果 它们就具有Is a关系 因为梨是水果 苹果也是水果 所以梨和苹果都可以从水果继承 获得所有水果都具有的通用特征 组合常用于描述类之间的 Has a 关系 即一个类拥有另外一些类 比如 图书馆有图书 汽车有发动机 车轮胎 座位等 计算机有CPU 存储器 显示器等 这些都可以用类的组合关系来实现 5 8继承与组合 例5 18 设计一个成绩单管理的程序 其中包括学生信息 如姓名 身高 性别 学号 专业等 学生学习某门课程后将得到一个成绩 相关的信息有课程名称 课程编号学分和成绩等 5 8继承与组合 经过抽象与继承 构造出图所示的类结构 5 8继承与组合 将每个类的声明与实现分别独立保存在与类同名的 h头文件和 cpp文件中 Course类 5 8继承与组合 Student类项目文件 TheEnd 谢谢大家 第6章多态性 多态性是面向对象程序设计语言的又一重要特征 它是指不同对象接收到同一消息时会产生不同的行为 继承所处理的是类与类之间的层次关系问题 而多态则是处理类的层次结构之间 以及同一个类内部同名函数的关系问题 简单地说 多态就是在同一个类或继承体系结构的基类与派生类中 用同名函数来实现各种不同的功能 6 1静态绑定和动态绑定 1 多态polymorphism对象根据所接收的消息而做出动作 同样的消息为不同的对象接收时可导致完全不同的行动 该现象称为多态性 简单的说 单接口 多实现2 联编一个程序常常会调用到来自于不同文件或C 库中的资源 如函数 对话框 等 需要经过编译 连接才能形成为可执行文件 在这个过程中要把调用函数名与对应函数 这些函数可能来源于不同的文件或库 关联在一起 这个过程就是绑定 binding 又称联编 6 1静态绑定和动态绑定 3 静态绑定与静态绑定静态绑定又称静态联编 是指在编译程序时就根据调用函数提供的信息 把它所对应的具体函数确定下来 即在编译时就把调用函数名与具体函数绑定在一起 动态绑定又称动态联编 是指在编译程序时还不能确定函数调用所对应的具体函数 只有在程序运行过程中才能够确定函数调用所对应的具体函数 即在程序运行时才把调用函数名与具体函数绑定在一起 4 多态性的实现编译时多态性 静态联编 连接 系统在编译时就决定如何实现某一动作 即对某一消息如何处理 静态联编具有执行速度快的优点 在C 中的编译时多态性是通过函数重载和运算符重载实现的 运行时多态性 动态联编 连接 系统在运行时动态实现某一动作 即对某一消息在运行过程实现其如何响应 动态联编为系统提供了灵活和高度问题抽象的优点 在C 中的运行时多态性是通过继承和虚函数实现的 6 1静态绑定和动态绑定 6 2函数重载 1 函数重载的意义函数重载意义在于用户可以通过同一个函数名访问某一类或一组相关操作的函数 由编译器决定具体的函数调用 有助于复杂问题的减化 2 函数重载的类型函数重载可分为普通函数重载 类成员函数重载以及继承结构中基类和派生类成员函数的重载几种情况 3 函数重载的要求要求重载函数具有不同的函数原型 即重载函数要在参数类型 参数个数或参数顺序上有所区别 不能有同一作用域内的两个同名函数具有完全相同的参数表 例6 1 重载复数的加法 实现复数实部与普通数据相加 两个复数相加的运算 includeusingstd cout usingstd endl classComplex doublereal imag public Complex doubler 0 doublei 0 real r imag i ComplexAdd doublex returnComplex x real imag ComplexAdd Complexc returnComplex real c real imag c imag voiddisplay cout real imag i endl doublegetReal returnreal doublegetImag returnimag ComplexAdd doubler Complexc return r c getReal c getImag ComplexAdd Complexc1 Complexc2 returnComplex c1 getReal c2 getReal c1 getImag c2 getImag voidmain Complexc1 2 3 c2 5 6 c3 c3 c1 Add 10 c3 display c3 c1 Add c2 c3 display c3 Add c1 c2 c3 display Add Complex Complex 6 3运算符重载 运算符重载是C 的一项强大功能 通过重载 可以扩展C 运算符的功能 使它们能够操作用户自定义的数据类型 增加程序代码的直观性和可读性 本章主要介绍类成员运算符重载与友元运算符重载 二元运算符与一元运算符重载 运算符 重载 this指针与运算符重载及流运算符 的重载 6 3 1运算符重载的概念 1 运算符重载的概念C 的运算符对语言预定义类型是重载的inti 2 3 doublej 2 4 8 floatf float 3 1 float 2 0 对于上面的3个加法表达式 C 系统提供了类似于下面形式的运算符重载函数 intoperator int int doubleoperator int double floatoperator float float 6 3 1运算符重载的概念 C 允许程序员通过重载扩展运算符的功能 使重载后的运算符能够对用户自定义的数据类型进行运算 比如 设有复数类Complex 其形式如下 classComplex doublereal image public 假设定义了下面的复数对象 并且要实现两个复数相加的运算 Complexc1 c2 c3 c1 c2 c3 why 使程序便于编写和阅读使程序定义类型与语言内建类型更一致how 使用特殊的成员函数使用自由函数 一般为友元 6 3 1运算符重载的概念 6 3 2运算符重载限制可以重新定义大多数运算符 newnew deletedelete 不能定义新的运算符不能重载某些特殊运算符 包括 sizeoftypeid不能改变运算符的目 优先级 结合性不能重载语言预定义类型的运算符含义如 无隐含重载 即 定义了 并隐含不定义 程序定义的含义与运算符固有含义吻合 6 3 3类外运算符重载 运算符可以非类成员的普通形式重载 运算符的计算结果是值 因此运算符函数是要返回值的函数 其重载的语法形式如下 返回类型operator 参数表 其中 operator是C 的保留关键字 表示运算符函数 代表要重载的运算符 它可以是前面列举的可重载运算符中的任何一个 例6 2 重载 运算符的功能 实现两个复数相加的运算 CH6 2 cpp includeusingstd cout usingstd endl structComplex doublereal imag Complex doubler 0 doublei 0 real r imag i voiddisplay cout real imag i endl Complexoperator Complexc1 Complexc2 Complext t real c1 real c2 real t imag c1 imag c2 imag returnt voidmain Complexc1 2 3 c2 5 6 c3 c3 c1 c2 c3 display 输出 7 9i 6 4类运算符的重载 1 C 为类默认的重载运算符 赋值运算 取类对象地址的运算函符 成员访问运算 如 和 这些运算符不需要重载就可以使用 但要在类中使用其他运算符 就必须明确地重载它们 6 4 1类成员运算符重载 1 非静态成员运算符重载以类成员形式重载的运算符参数比实际参数少一个 第1个参数是以this指针隐式传递的 classComplex doublereal image public Complexoperator Complexb 6 4 1类运算符的重载 2 友元运算符重载如果将运算符函数作为类的友元重载 它需要的参数个数就与运算符实际需要的参数个数相同 比如 若用友元函数重载Complex类的加法运算符 则形式如下 classComplex friendComplexoperator Complexa Complexb 声明 Complexoperator Complexa Complexb 定义 6 4 1 1重载二元运算符 1 二元运算符的调用形式与解析aa bb可解释成aa operator bb 或解释成operator aa bb 如果两者都有定义 就按照重载解析classX public voidoperator int X int voidoperator X X voidoperator X double voidf Xa a 2 a operator 2 2 a operator X 2 a a 2 0 operator X double 6 4 1 1重载二元运算符 1作为成员函数重载作为类的非静态成员函数的二元运算符 只能够有一个参数 这个参数是运算符右边的参数 它的第一个参数是通过this指针传递的 其重载形式类似于下 classX T1operator T2b 其中 T1是运算符函数的返回类型 T2是参数的类型 原则上T1 T2可以是任何数据类型 但事实上它们常与X相同 6 4 1 1重载二元运算符 例6 3 有复数类Complex 利用运算符重载实现复数的加 减 乘 除等复数运算 CH6 3 cpp includeusingnamespacestd classComplex private doubler i public Complex doubleR 0 doubleI 0 r R i I Complexoperator Complexb Complexoperator Complexb Complexoperator Complexb Complexoperator Complexb voiddisplay ComplexComplex operator Complexb returnComplex r b r i b i ComplexComplex operator Complexb returnComplex r b r i b i ComplexComplex operator Complexb Complext t r r b r i b i t i r b i i b r returnt ComplexComplex operator Complexb Complext doublex x 1 b r b r b i b i t r x r b r i b i t i x i b r r b i returnt voidComplex display cout0 cout if i 0 cout i i endl display voidmain void Complexc1 1 2 c2 3 4 c3 c4 c5 c6 c3 c1 c2 c4 c1 c2 c5 c1 c2 c6 c1 c2 c1 display c2 display c3 display c4 display c5 display c6 display 对于程序中的运算符调用 c3 c1 c2 c4 c1 c2 c5 c1 c2 c6 c1 c2 C 会将它们转换成下面形式的调用语句 c3 c1 operator c2 c4 c1 operator c2 c5 c1 operator c2 c6 c1 operator c2 实际上 在程序中也可以直接写出这样的表达式 显式调用重载的运算符函数 6 4 1 2重载一元运算符 1 一元运算符一元运算符只需要一个运算参数 如取地址运算符 负数 自增加 等 2 一元运算符常见调用形式为 a或a 隐式调用形式a operator 显式调用一元运算符 其中的 代表一元运算符 a代表操作数 a代表前缀一元运算 如 a a 表示后缀运算 如 a 3 a将被C 解释为下面的形式之一a operator operator a 6 4 1 2重载一元运算符 一元运算符作为类成员函数重载时不需要参数 其形式如下 classX Toperator T是运算符 的返回类型 从形式上看 作为类成员函数重载的一元运算符没有参数 但实际上它包含了一个隐含参数 即调用对象的this指针 6 3 1作为成员函数重载 例6 4 设计一个时间类Time 它能够完成秒钟的自增运算 CH6 4 cpp includeusingnamespacestd classTime private inthour minute second public Time inth intm ints Timeoperator voiddisplay Time Time inth intm ints hour h minute m second s if hour 24 hour 0 若初始小时超过24 重置为0if minute 60 minute 0 若初始分钟超过60 重置为0if second 60 second 0 若初始秒钟超过60 重置为0 TimeTime operator second if second 60 second 0 minute if minute 60 minute 0 hour if hour 24 hour 0 return this voidTime display cout hour minute second endl voidmain Timet1 23 59 59 t1 display t1 隐式调用方式t1 display t1 operator 显式调用方式t1 display 6 4 2类的友元运算符重载 1 关于友元重载运算符为了实现类对象的各种运算 除了将运算符重载为类的成员函数外 还可以将它重载为类的友元函数 在有些情况下 只有将运算符重载为类的友元才能解决某些问题 比如 对于例6 1的复数类而言 假设有下面的加法运算 Complexa b 2 3 a b 2 正确a 2 b 错误 6 4 2类的友元运算符重载 2 重载二元运算符为类的友元函数时需要两个参数 其形式如下 classX friendT1operator T2a T3b T1operator T2a T3b T1 T2 T3代表不同的数据类型 事实上它们常与类X相同 例6 5 对于例6 3中的复数类Complex 利用友元运算符重载实现复数的加 减 乘 除等复数运算 CH6 5 cpp include usingnamespacestd classComplex private doubler i public Complex doubleR 0 doubleI 0 r R i I friendComplexoperator Complexa Complexb friendComplexoperator Complexa Complexb friendComplexoperator Complexa Complexb friendComplexoperator Complexa Complexb voiddisplay Complexoperator Complexa Complexb returnComplex a r b r a i b i Complexoperator Complexa Complexb returnComplex a r b r a i b i Complexoperator Complexa Complexb Complext t r a r b r a i b i t i a r b i a i b r returnt Complexoperator Complexa Complexb Complext doublex x 1 b r b r b i b i t r x a r b r a i b i t i x a i b r a r b i returnt voidComplex display cout0 cout if i 0 cout i i endl voidmain void Complexc1 1 2 c2 3 4 c3 c4 c5 c6 c3 c1 c2 c4 c1 c2 c5 c1 c2 c6 c1 c2 c1 display c2 display c3 display c4 display c5 display c6 display 6 4 2类的友元运算符重载 3 说明 对于不要求左值且可以交换参数次序的运算符 如 等运算符 最好用非成员形式 包括友元和普通函数 的重载运算符函数实现 对于前面分析过的 2 c2 和 c2 2 之类的对称运算表达式 也可以直接通过友元运算符重载实现 例 用友元运算符重载实现复数与实数的加法运算 实数与复数的实部相加 复数的虚部保持不变 CH cpp includeclassComplex private doubler i public Complex doubleR 0 doubleI 0 r R i I friendComplexoperator Complexa doubleb returnComplex a

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论