C++应用与开发案例教程(下)ppt.ppt_第1页
C++应用与开发案例教程(下)ppt.ppt_第2页
C++应用与开发案例教程(下)ppt.ppt_第3页
C++应用与开发案例教程(下)ppt.ppt_第4页
C++应用与开发案例教程(下)ppt.ppt_第5页
已阅读5页,还剩233页未读 继续免费阅读

下载本文档

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

文档简介

C 应用与开发案例教程 下 第8章异常处理 有时候 你可能面临并非所有的事情都是完美的事实 偶而 甚至最好的计划也会失败 不管怎么说 是人就会犯错 为了防范运行时的错误 C 环境给予程序员试运行的机会 如果运行失败 它提供了一个预防机制 异常处理就是这种机制 异常是发生在运行期间的诸如零作分母 数组的溢出以及空闲空间的耗尽这样的错误事件 假如这些错误没有被捕获 则程序会突然地停止执行 而没有提供任何指示给使用者 告诉他发生了什么 处理这样的异常 不同的程序员有不同的风格 这导致了代码的多样性 这种多样性随着用户自定义类的使用而增长 因为每一种这样的类都带有其潜在的特定类的异常结构 当一个异常发生时 C 以下列方式做出反应 1 发生异常的函数可以产生一个系统定义的消息 8 1概述 8 1概述 2 函数可以完全终止执行 3 函数可以跳过中间层继续执行其他的部分 C 语言提供内建的对于意外情况处理的支持 这就是异常处理 通过C 语言的异常处理 你的程序能够使意外的事件与高层次的执行上下文进行通信 它能够从这种异常事件中恢复过来 C 处理异常的机制是灵活的 因此它能处理任何类型的异常 在C 中 产生一个异常被称为引发一个异常 8 2何时使用异常处理 在程序运行期间调用一个函数会产生三种结果 它们是 1 正常执行在执行过程中 函数正常地执行并返回到调用的程序中 一些函数把结果代码返回给调用者 返回的代码表明执行的结果 2 错误执行当调用者在变量传递过程中犯错或调用的函数超出上下文的范围时 会导致一个错误 这会终止程序的执行 3 异常执行在执行过程中出现异常的一种典型情况是内存不足 另一种典型情况是一个I O操作的异常 例如 当打开一个存在的文件时 存储媒介的设备驱动器可能会出错 这种情况不能通过简单的查找文件是否存在来处理 引发并捕捉异常是最佳的处理方法 8 2何时使用异常处理 在开发需要照顾意外情形的应用程序时 异常处理是必需的 发生在程序执行时的意外情形有 运行内存不够 资源分配错误 不能找到 打开文件等 假如这些错误没有被捕获 那么 在没有给予用户任何有关发生什么情况的提示下 程序将被中断 异常可以被定义为在程序执行中意外情况的出现和正常指令流的中断 8 3异常处理的基本语法 异常处理是C 的一个特征 它提供了处理运行期间异常的标准工具 一种一致性语法可以应用于这种目的 它能够被程序员很好的协调校正 一些常见的异常有分母为零 数组溢出以及NULL指针 即一个包含0值的指针 的引用 在C 语言中用try throw和catch语句实现异常处理 在程序的某个函数中发现异常时使用throw表达式抛出这个异常 这个异常被抛给该函数的调用者 调用者用catch捕获该异常并进行相应的处理 当预测到某段程序将可能出现问题时 则该段程序放在try语句之后 使该段程序得以有机会抛出异常 8 3 1异常处理的语法 异常处理的语法如下 throw表达式语法 throw表达式try表达式语法 try受保护的程序段语句catch 异常类型 异常处理语句 catch 异常类型 异常处理语句 其中 catch括号中异常类型与throw抛出的异常类型相匹配 当某个异常发生后 catch语句将逐个被检查 直到某个异常类型与throw抛出的异常类型相匹配 然后执行catch后面的 8 3 1异常处理的语法 异常处理程序 若catch括号中是省略号 则该catch可以捕获所有的异常类型 所以这样的语句一般放在所有catch语句的最后一个 否则其他catch语句就没有机会捕捉相应的错误类型了 当某个异常被捕捉并被相应的程序段处理后 系统将继续执行捕捉函数的其余部分 产生异常的函数和直到捕捉到异常的函数之间的所有调用链上的函数将从栈中删除 如果一个异常没有被任何函数捕捉到 则运行函数terminate将被调用 该函数的默认功能是调用abort函数终止程序的运行 一个异常处理的过程可以如下表示 voidfun inti doublef 8 3 1异常处理的语法 charc 程序的其他部分try 预期可能出现问题的地方 保护起来 throwi 如果出现问题1 抛出一个整数 只是假设 throwf 如果出现问题2 抛出一个双精度数 只是假设 throwc 如果出现问题3 抛出一个字符 只是假设 catch intii 相应处理 catch doubleff 8 3 1异常处理的语法 相应处理 catch charcc 相应处理 程序其他部分 例8 1 includevoidmain try 8 3 1异常处理的语法 intage cout age if age 100 age 1 throw Invalidage cout Afterthethrowstatement endl catch char msg cout Error msg endl cout Afterthecatchhandler endl 程序的输出结果为 8 3 1异常处理的语法 Entertheage 46AfterthethrowstatementAfterthecatchhandleEntertheage 116Error Invalidage Afterthecatchhandle 8 3 2异常的类型 异常有两种类型 同步异常和异步异常 1 同步异常发生在运行期间 并且能够使用throw表达式产生 仅有这种类型的异常能够使用C 中的异常处理器处理 控制不必返回异常引发处 2 异步异常因为一个键盘或鼠标的中断而产生 C 程序不能使用try和catch语句直接处理这种类型的异常 一次不得不产生一个自动调用与键盘或鼠标中断相关的例程 8 4Try catch和throw语句 在C 语言中执行异常处理的是Try catch和throw语句 通过C 的异常处理 你的程序能够探测到异常事件并从中恢复过来 一个包含try catch和throw语句的程序的执行过程如下 1 控制通过正常的连续执行到达try语句 在try模块内的语句被保护执行 2 如果在被保护的执行期间没有异常被引发 try模块之后的catch语句不会执行 在引发异常的try模块后面的最后的catch语句之后继续执行 3 如果在被保护的执行过程中有一个异常被引发 程序搜寻一个能够处理被引发类型的异常的catch语句 4 如果没有找到一个类型匹配的处理器 那么调用预定义的运行期间的函数terminate 8 4Try catch和throw语句 5 如果找到了类型匹配的catch处理器 初始化它的参数并执行catch处理器中的指令 如果程序终止 释放堆栈中的所有值 一个基类的处理器也能够处理它的子类的异常 如果在catch表达式中遇到了一个 它将处理所有的异常而不管它们的数据类型 例8 2 includevoidmain cout beforethethrowstatement endl try throw25 6f 引发一个浮点值 8 4Try catch和throw语句 catch char str cout Caughtastring endl catch inti cout CaughtanInteger endl catch cout Unknowndatatype endl cout Afterthethrowstatement endl 8 4Try catch和throw语句 在上面的代码中 一个浮点值从try模块中被引发 然而 没有为接受一个浮点值而特意设计的catch处理器 因此程序的输出是 beforethethrowstatementUnkonwndatatypeAfterthethrowstatement新的运算符new引发了一个bad alloc异常 新的头文件声明了bad alloc类 它从异常类继承而来 What 函数在异常类中被声明 例8 3 检查内存分配操作 include includevoidmain 8 4Try catch和throw语句 char buff try buff newchar 1000000000 声明在新的头文件中的新的运算符bad alloc引发了一个类的对象 catch std bad alloc 该程序是关于一个try模块和与它相联系的catch处理器的 8 4Try catch和throw语句 一个实例 该实例检查一个内存分配操作的错误 如果内存分配操作成功 则catch处理器不执行 在一个内存分配错误的情况下 程序的输出是 Exceptionraised bad alloc如果在一个try模块执行过程中没有引发异常 在try模块后的catch语句不被执行 程序将在引发异常的try模块之后的最后一条catch语句继续执行 控制器只能够通过一个引发的异常来输入一个catch处理器 而不能通过一个switch语句中的一个case标号来进行 在前面提到的实例中 在try模块后仅存在一个catch处理器 然而 并不是一直这样的 也会存在其他的catch处理器 如果throw语句准备仅引发内建的数据类型 在这种情况下使用字符型 catch处理器的数量不能超过语言支持 8 4Try catch和throw语句 的内建数据类型的数量 为了解决这个问题 一个用户定义类型的不同的对象能够引发给处理器 如下例 例8 4 include includeclassMyException public charcause 21 MyException char in 0 strcpy cause in 8 4Try catch和throw语句 voidprintMessage cout age if age100 8 4Try catch和throw语句 MyExceptione Problemwithage throwe else cout CORRECTAGE endl voidmain Aclassobject 8 4Try catch和throw语句 try object get catch MyException 例8 5 异常的抛出和捕捉在同一函数中 include includeclassPerson 8 4Try catch和throw语句 protected charName 20 姓名intOld 年龄unsignedlongID 身份证号charSex 性别public Person char theName inttheOld unsignedlongtheId chartheSex 类Person的构造函数 Person Person char theName inttheOld unsignedlongtheId chartheSex if Name 有效性检查 8 4Try catch和throw语句 throw InvalidPerson sname if Old200 有效性检查throwOld if Sex M 8 4Try catch和throw语句 Personp3 1 33333333L M 类Person的对象p3cout funline4 endl Personp4 1 44444444L F 类Person的对象p4 catch char 捕捉错误并进行处理 cout InvalidSex endl catch int 捕捉错误并进行处理 cout InvalidOld endl catch char Msg 捕捉错误并进行处理 8 4Try catch和throw语句 cout InvalidName endl voidmain cout beforefun endl fun cout endfun endl cout inthemainfunction endl 在本程序中 异常的抛出和捕捉都在函数fun中 在实际应用中 异常的引发和处理一般在不同函数中 这样低层函数可以专注于具体问题 上层调用函数可以在恰当的位置设计对不同类型 8 4Try catch和throw语句 的异常进行各种处理而不考虑低层函数是如何产生这个异常的 例8 6 对 例8 5 稍加改动 使异常的抛出和捕捉在不同函数中进行 例8 6 异常的抛出和捕捉在不同函数中 include includeclassPerson protected charName 20 姓名intOld 年龄unsignedlongID 身份证号charSex 性别 8 4Try catch和throw语句 public Person char theName inttheOld unsignedlongtheId chartheSex 类Person的构造函数 Person Person char theName inttheOld unsignedlongtheId chartheSex if Name 有效性检查throw InvalidPerson sname if Old200 有效性检查throwOld if Sex M Sex m Sex F Sex f 有效性检查 8 4Try catch和throw语句 throwSex voidfun cout funline1 endl Personp1 zhang 43 1234566L M 类Person的对象p1cout funline2 endl Personp2 1 22222222L F 类Person的对象p2cout funline3 endl Personp3 1 33333333L M 类Person的对象p3cout funline4 endl Personp4 1 44444444L F 类Person的对象p4 8 4Try catch和throw语句 voidmain try cout beforefun endl fun cout endfun endl catch char 捕捉错误并进行处理 cout InvalidSex endl catch int 捕捉错误并进行处理 8 4Try catch和throw语句 cout includeclassString 8 4Try catch和throw语句 private char str public String str 0 String char inString str newchar strlen inString 1 strcpy str inString voidreplace charsearch charrepl 8 4Try catch和throw语句 intcounter for counter 0 str counter 0 counter if str counter search str counter repl voiddisplay cout str 8 4Try catch和throw语句 voidmain StringstrObject 该对象不包含任何东西strObject replace strObject display 上面的代码产生下面的运行期错误 Segmentationfault coredumped 分析 要想纠正如上程序运行期错误 首先要找到出现异常的原因 然后采用异常处理机制去处理异常 因此 下面分两步进行介绍 1 标识异常的原因根据分析知道 在main 函数中 String类的一个空对象被 8 4Try catch和throw语句 创建 replace 函数将遍历字符串直到空字符 0 出现 但如果字符串为空 那么操作失败 2 标识异常处理的机制采用try throw catch语句进行处理 修改上面程序得如下程序 include includeclassString private char str intcheck if str NULL 8 4Try catch和throw语句 return0 elsereturn1 public String str 0 String char inString str newchar strlen inString 1 strcpy str inString 8 4Try catch和throw语句 voidreplace charsearch charrepl if check 0 throw NULLpointerexception intcounter for counter 0 str counter 0 counter if str counter search str counter repl 8 4Try catch和throw语句 voiddisplay cout str voidmain StringstrObject 对象不能包含任何的trytry strObject replace 引起replace函数异常strObject display 8 4Try catch和throw语句 catch char message cout Exception message endl 程序的输出结果如下 Exception NULLpointerexception 8 5标准C 库中的异常类 标准C 库中包含9个异常类 它们可以分为运行时异常和逻辑异常 length error 运行时异常 长度异常domain error 运行时异常 域异常out of range error 运行时异常 越界异常invalid argument 运行时异常 参数异常range error 逻辑异常 范围异常overflow error 逻辑异常 溢出 上 异常underflow error 逻辑异常 溢出 下 异常标准C 库中的这些异常类并没有全部被显式使用 因为C 标准库中很少发生异常 但是这些标准C 库中的异常类可以为编程人员 特别是自己类库的开发者提供一个指导 下面程序简单说明C 标准异常类的使用 例8 8 演示标准异常类的使用 8 5标准C 库中的异常类 include includeusingnamespacestd voidmain try exceptiontheError 声明一个C 标准异常类的对象throw theError 抛出该异常类的对象 catch constexception 用what 成员函数显示出错原因 8 5标准C 库中的异常类 try logic errortheLogicError LogicError 声明一个C 标准异常类logic error的对象throw theLogicError 抛出该异常类的对象 catch constexception 用what 成员函数显示出错原因 8 6程序实例 例8 9 设计一个异常Exception抽象类 在此基础上派生一个OutOfMemory类响应内存不足 派生一个RangeError类响应输入的数不在指定范围内 实现并测试这两个类 程序如下 EG8 8 CPP includeclassException public Exception virtual Exception 8 6程序实例 virtualvoidPrintError 0 classOutOfMemory publicException public OutOfMemory OutOfMemory 8 6程序实例 virtualvoidPrintError voidOutOfMemory PrintError cout OutofMemory endl classRangError publicException public RangError unsignedlongnumber BadNum number 8 6程序实例 RangError virtualvoidPrintError virtualunsignedlongGetNumber returnBadNum virtualvoidSetNumber unsignedlongnumber BadNum number 8 6程序实例 private unsignedlongBadNum voidRangError PrintError cout NumberOutofrange Youused GetNumber n voidfn1 unsignedint fn2 voidfn3 unsignedint voidmain try 8 6程序实例 fn1 catch Exception 8 6程序实例 returnn voidfn1 unsignedint p fn2 fn3 p cout Number 8 6程序实例 if Number 1000 Number 0 throwRangError Number p Number 程序的输出结果为 Enteraninteger 0 1000 78Thenumberis 78Enteraninteger 0 1000 1500Numberoutofrange Youused1500 下面给出一个接近实用的例子 假设开发一个人员管理系统 并且声明了人这个基本类Person 它包含姓名 性别 年龄 证件号码等数据 我们可以事先估计一下可能出现的异常情况 如用户忘记输入姓名 年龄不在正常范围内 证件号码不符合要求 性别不符合要求等等 这要求有一个可以报告这类错误 8 6程序实例 的类InvalidPerson 同时 用户输入数据进行处理完成后需要存盘 下一次可以从磁盘中调出这些数据 这个过程中可能的异常是输入输出文件打开出错 输入数据被破坏等 要求有一个可以报告错误的类IOError 看下面的例子并体会异常处理的用法 例8 10 人员管理系统中常用的异常处理 EG8 9 H ifndefEG8 9 H defineEG8 9 Husingnamespacestd classPerson classError 声明出错类 8 6程序实例 public Error constchar theWhere constchar theWhy why theWhy where theWhere virtualvoiddisplay ostream endif EG8 9 CPP include include 8 6程序实例 include EG8 9 H classInvalidPerson publicError 从类Error派生类InvalidPerson public InvalidPerson constPerson thePerson constchar theWhy Error Person InvalidCheck theWhy pPerson thePerson InvalidPerson类构造函数virtualvoiddisplay ostreamclassIOError publicError 从类Error派生类IOError 8 6程序实例 public IOError constchar theWhere constchar theWhy constchar theFileName IOError构造函数Error theWhere theWhy FileName theFileName virtualvoiddisplay ostreamclassPerson public 8 6程序实例 Person conststring 重载 运算符 8 6程序实例 voidError display ostream 显示本类的新增特性 voidIOError display ostream out const IOError display函数的实现 8 6程序实例 Error display out 调用基类的显示函数out File FileName endl 显示本类的新增特性 Person Person conststring 8 6程序实例 抛出InvalidPerson if Old200 如果人员年龄不在0 200之间 该人员信息无效 throwInvalidPerson this Invalidperson sOld 抛出InvalidPersonif Id 0L 如果身份证号码小于0 该人员信息无效 throwInvalidPerson this Invalidperson sId 抛出InvalidPerson if Sex M Sex m Sex F Sex f 如果性别不是字符 M m F f 该人员信息无效 8 6程序实例 throwInvalidPerson this Invalidperson sSex 抛出InvalidPerson voidPerson display ostream 8 6程序实例 voidPerson output ostream 8 6程序实例 voidPerson input istreamtry if in get 提取的数据不正确或者到了文件尾 if in eof 8 6程序实例 return Person 0 throwError Person input Invalidinitialchar 如果数据不正确 则抛出Error getline in TempPersonName 将到 为止的流in中内容保存到TempPersonName中in TempPersonOld TempPersonID TempPersonSex 从流in中依次输入到各个变量中in get 略过本行中剩余部分 到达下一行TempPerson newPerson TempPersonName TempPersonOld TempPersonID TempPersonSex 8 6程序实例 用于输入数据构造临时对象TempPerson InvalidCheck 有效性检查catch Error 8 6程序实例 EG8 9 CPP include include include EG8 9 H ostream 将类Person的若干个对象输出到文件中 可放于自己指定的目录下 8 6程序实例 InputPerson d C temp person txt 从文件中输入类Person对象并显示 catch Error 重载 运算符的实现 8 6程序实例 thePerson dispaly out returnout voidOutputPersons char theFileName 将人员信息输出到文件的函数 ofstreamout theFileName if out fail 如果文件打开出错 抛出IOError类型的对象 throwIOError OutputPersons can topenoutputfile theFileName Personwang wang 25 750308206L M 类Person的对象wangwang output out 输出 可能抛出类IOError的对象 以下同 8 6程序实例 Personzhang zhang 35 700308205L F zhang output out Personyang yang 26 800808107L F yang output out Personzhao zhao 25 40600211004L M zhao output out voidInputPersons char theFileName 从文件中获得类Person对象并进行显示的函数 inti 0 ifstreamin in open theFileName ios in if in fail 如果文件打开出错 抛出IOError类型的对象 8 6程序实例 throwIOError InputPersons can topeninputfile theFileName Person TempPerson while in eof 从文件中输入对象并显示该对象 if TempPerson Person input in cout PersonNo i TempPerson endl deleteTempPerson 源程序结束 第9章模板 大多数人或多或少都看过或听说过印刷的过程 不管是印在布上还是在报纸上 通常要用到一个样本 印刷的一种方法是为每一页纸或每一米布一遍遍地重复制作样本 另一种更常见也是更广泛使用的方法是使用印版或模子 创建一个这种样本的模子或模板 用墨水或颜料对它染色 然后把这个样本印到所需要的媒质上 因此 做一个模子可以避免重复制作样本的麻烦 这个模子可以用来印刷任意多次 在C 中 模板就相当于这里说的模子 模板的实质是函数和类根据要求在运行时的实例化 这意味着程序员在编写代码时不必了解确切的应用环境 类的最终用户能够在执行的时候创建自定义的类 模板的概念可以应用于函数和类 模板是C 支持参数化多态的工具 使用模板可以使用户或者函数声明一种一般模式 使得类中的某些数据成员或者 9 1概述 9 1概述 成员函数的参数 返回值取得任意类型 9 1概述 9 2 1函数模板和模板函数在C 中 当一个函数被重载时必须创建它的多个拷贝 每一个拷贝对应它所作用的一种数据类型 看一下max 函数的实例 它返回传递给它的两个数中较大的一个 对每一种要用到的数据类型都要编写函数代码 因此 你得为每一种数据类型编写同样的函数代码 例如int型 float型 char型 double型 下面是max 函数的几个对应不同数据类型的代码版本 intmax inta intb returna b a b charmax chara charb returna b a b 9 2 1函数模板和模板函数 doublemax doublea doubleb returna b a b floatmax floata floatb returna b a b 可以看出 这样的四个函数的函数体都是相同的 其功能几乎完全一样 只是返回值或者参数不同 对不同的数据类型 相同的代码必须重写多次 而执行的是相同的功能 这是对时间和精力的浪费 为避免这种浪费 C 为我们提供了一个方便的语法规则 函数模板 利用函数模板 可以建立一个具有通用功能的函数 支持不同的函数参数和返回值 9 2 1函数模板和模板函数 函数模板的语法形式如下 template 函数定义 其中T代表在函数模板中要使用的通用类型 在该函数的调用过程中 T被具体化 例如对上面的重载函数 我们只要声明一个函数模板就可以了 templateTmax Ta Tb returna b a b 9 2 1函数模板和模板函数 关键字template表明正在声明一个模板 数据类型参数T由模板参数给出 该模板的含义为 无论模板参数T实例为int float char或任意其他类型 包括类类型时 函数max 就为实例化了的参数求最大值 这样定义的max 函数在进行求最大值的操作时 首先必须将模板参数T实例化 从这个意义上说 这里定义的max 函数并不是一个完全的函数 我们称它为函数模板 因此 函数模板代表了一类函数 但是它不是一个完全的函数 必须将其模板参数T实例化后 才能完成具体函数的功能 将T实例化的参数常常称为模板实参 用模板实例化的函数称为模板函数 例如 voidfun inti Mclassx y 9 2 1函数模板和模板函数 intj max i 0 模板实参为整数类型Mclassm max x y 模板实参为Mclass类型 这里生成了两个模板函数max i 0 和max x y max i 0 用模板实参int将类型参数T实例化 而max x y 将T实例化为Mclass类类型 一个函数模板提供一类函数的抽象 它以任意类型T为参数 由一个函数模板产生的函数称为模板函数 它的函数模板的具体实例 就象类与对象一样 函数模板将具有相同程序正文的一类函数抽象出来 可以适应任意类型T 函数模板对某一特定类型的实例就是模板函数 函数模板代表了一类函数 模板函数表示某一具体的函数 图9 1给出了函数模板和模板函数的关系 9 2 1函数模板和模板函数 9 2 1函数模板和模板函数 例9 1 编写函数模板max 使得能找出任意类型的两个数中的最大数 includetemplatetypemax typea typeb returna b a b voidmain cout max A a max A a endl cout max 30 40 max 30 40 endl cout max 45 67f 12 32f max 45 67f 12 32f endl 9 2 1函数模板和模板函数 程序的输出结果为 max A a amax 30 40 40max 45 67f 12 32f 45 67 例9 2 编写能完成计算任意类型数的平方的函数模板 includetemplatetypesquare typea typeb b a a returnb voidmain 9 2 1函数模板和模板函数 cout square 23 45f square 23 45f endl cout square 43 square 43 endl 程序的输出结果为 square 23 45f 549 903square 43 1849 9 2 2重载函数模板 在有些特殊情况下需要对函数模板进行重载 C 允许函数模板被一个或多个同名的非模板函数重载 考虑下面的例子 例9 3 templateTmax Ta Tb return a b a b voidf inti charc max i i max c c max i c 错误 max Int char 无法匹配 9 2 2重载函数模板 max c i 错误 这里出现的错误在于 模板类型并不知道int和char之间能进行隐式类型转换 但是 这样的转换在C 中是很普通的 为了解决这个问题 C 允许函数模板可以参与重载 用户可以用一个非模板函数重载一个同名的函数模板 例如 可以这样定义 templateTmax Ta Tb return a b a b intmax int int 显式地声明函数max int int 不是模板函数 9 2 2重载函数模板 voidf inti charc max i i 调用函数max int int max c c 调用模板max char char max i c 调用函数max Int int max c i 调用函数max int int 这里 非模板函数max int int 重载了上述的函数模板max Ta Tb 当出现调用语句max i c 和max c i 时 它执行的是重载的非模板函数版本max int int 还可以定义如下函数 9 2 2重载函数模板 charmax char x char y return strcmp x y 0 x y 非模板函数max ichar x char y 也重载了上述的函数模板 当出现调用语句max a b 时 它执行的是这个重载的非模板函数的版本 其中a和b都是字符串变量 在C 中 函数模板与同名的非模板函数的重载方法遵循下列约定 寻找一个参数完全匹配的函数 如果找到了 就调用它 寻找一个函数模板 将其实例化产生一个匹配的模板函数 如果找到了 就调用它 试一试低一级的对函数的重载方法 如通过类型转换可 9 2 2重载函数模板 产生参数匹配等 如果找到了 就调用它 如果1 2 3均未找到匹配的函数 那么这个调用是一个错误 如果在第一步有多于一个的选择 那么这个调用是意义不明确的 也会产生一个错误 以上重载模板函数的规则 可能会引起许多不必要的函数定义的产生 但一个好的实现应该是充分利用这个功能的简单性来抑制不合逻辑的回答 9 3类模板和模板类 一个类模板 也称类属类或类生成类 允许用户为类定义一种模式 考虑如下向量类 一维数组 的例子 不管整形向量还是任何其他类型的向量 在所有类型上进行的基本操作是相同的 如插入 删除 检索等 由于将每个元素的类型当做一个类的类型参数来对待 系统可快速生成类型安全的类定义 类模板定义语法 templateclassclass name public type1var1 9 3类模板和模板类 例9 4 includetemplateclassVector T data intsize public Vector int Vector delete data 9 3类模板和模板类 T其中 关键字template表示正在声明一个模板 其类属参数由模板参数给出 这时 类Vector声明了一个数组类 当T被实例化为int char float string或complex甚至任意类型Myclass时 Vector被实例化为整形数组 字符数组 浮点数数组 字符串数组或复数数组甚至任意类型Myclass的数组 从这个意义上说 Vector是一个不完全的类 这时Vector被称为类模板 Vector是该类模板的名字 将模板参数实例化的参数常称为模板实参 例如 int char等 用 9 3类模板和模板类 模板实参生成的类称为模板类 例如 Vector是一个模板类 因此 类外成员函数定义的语法为 templateVector Vector data newT n size n 模板外的成员函数定义都由template开始 表示是一个类模板的成员函数 其类模板的类名为Vector 下面的main程序说明了如何使用类模板 voidmain 9 3类模板和模板类 Vectorx 5 产生一个整形向量for inti 0 ix 5 由模板类Vector声明一个对象x 它是具有5个元素的整形向量 由于在Vector中重载了运算符 所以与一般数组的使用方法没有什么区别 Vector还可以这样用 Vectorv1 10 9 3类模板和模板类 Vectorv2 20 TypedefVectorcvec cvecv3 30 v1 3 7 v2 3 complex 7 8 这里Vector和Vector都是模板类 它说明了一个整形向量v1 10 两个复数类型的向量v2 20 和v3 30 图9 2给出了类模板与模板类的关系 9 3类模板和模板类 9 3类模板和模板类 尽管上述例子都只使用了一个模板参数 实际上允许使用多个参数 模板参数可以是类型 也可以是非类型的数据 综上所述 一个类模板说明了单个类怎样建立 就好像类型声明说明了单个对象是怎样建立的一样 当用户需要编写几乎相等的代码时 可以使用模板 可以看出 定义一个模板是一件简单的事 不管是函数模板还是类模板 必须明确规定其模板参数 因此 模板说明语句template中的关键字template表示正在声明一个模板 模板参数规定了类属参数 模板参数可以由一个或多个由逗号隔开的参数组成 参数可以是类型 如classT class后跟一个标志符组成 也可以是非类型参数 即一般的参数 9 3类模板和模板类 从语法上讲 要说明一个函数模板 只需将模板说明语句放在一个函数说明的前面 并将相应的参数改为模板参数即可 要说明一个类模板 除了将模板说明语句冠以类说明之前以及设置类中相应的模板参数之外 还要将类模板的名字与模板参数 用尖括号括起来的参数串 一起使用 例如 Vector代表类模板名Vector代表模板类名假设需要对同一数据类型定义两个不同的类 下面代码可以实现 例9 5 includetemplateclassw 9 3类模板和模板类 public Ta w Tq a z q coutone 100 w twoptr 9 3类模板和模板类 wfour 100 45 程序的输出结果为 a 110a 230a 140 45注意 在该实例中 给出了一个基本数据类型作为模板参数 在模板类中 这是允许的 在main 函数的第一和第三行创建的对象属于同一个类 第四行用了一个float型的参数 使用这种表达式 基本数据类型值可以用在对象声明中 9 4程序实例 用模板进行程序设计对许多人是陌生的 例9 6 从一个简单问题入手 介绍了怎样将系统的各个部分按概念特性的不同而划分成块 pieces 当研究这些块时 应该考虑如何将这里所用的技术也应用到更大的程序中 例9 6 将一个数组的元素进行累加 intsum int p intn intresult 0 for inti 0 i n i result p i returnresult 可以使用这样的函数 7 2 4用友元函数重载运算符 includeintsum int p intn intresult 0 for inti 0 i n i result p i returnresult voidmain intx 10 for inti 0 i 10 i x i i 7 2 4用友元函数重载运算符 cout sum x 10 endl 这个例子输出45 它是小于10的非负整数的和 sum函数知道三件事 它将一组东西加在一起 正在相加的东西是整数 正在相加的整数按一种特殊的方法存储 看一看怎样划分该程序 以便上述每个特性都能用模板的方法来表达 1 分离出迭代操作 iteration 首先假定要把被相加的元素存于数组中 为了遍历数组 即在这个数组上进行迭代操作 要遵循标准的C 技术 使迭代成为一个类 显然 迭代所需的操作为 7 2 4用友元函数重载运算符 1 一个构造函数 它建立要被处理的数据 2 请求数组中下一个元素的方法 3 区分迭代什么时候结束的方法 4 赋值运算符函数 拷贝构造函数和析构函数 迭代对象常常用于C 类库 它们常常被称为迭代算子 iterators 有许多种迭代算子 这里仅仅给出它们最基本的轮廓 将至今所知道的关于迭代算子的操作写出来 classInt iterator public Int iterator int int intvalid const intnext 7 2 4用友元函数重载运算符 Int iterator constInt iterator 7 2 4用友元函数重载运算符 while ir valid result ir next returnresult 使用这个sum函数的main程序可能会是这样的 includevoidmain intx 10 for inti 0 i 10 i x i i cout sum Int iterator x 10 endl 7 2 4用友元函数重载运算符 唯一的差别是 不是用sum x 10 而是用sum Int iterator x 10 当然 也可以写一个重载的函数 它能维持原来的接口界面 inlineintsum int p intn returnsum Int iterator p n 现在回到Int iterator类的定义上来 进一步细化 这个类的对象实际需要什么信息呢 需要能确定数组中下一个元素的位置 假设就用指针int 另外还需要确定数组是否结束 这可以用序列中元素的数目来描述 因此 Int iterator类的私有段成员可描述为 private int data 7 2 4用友元函数重载运算符 intlen 构造函

温馨提示

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

评论

0/150

提交评论