C应用与开发案例教程下课件_第1页
C应用与开发案例教程下课件_第2页
C应用与开发案例教程下课件_第3页
C应用与开发案例教程下课件_第4页
C应用与开发案例教程下课件_第5页
已阅读5页,还剩469页未读 继续免费阅读

下载本文档

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

文档简介

第8章异常处理第8章异常处理

有时候,你可能面临并非所有的事情都是完美的事实,偶而,甚至最好的计划也会失败。不管怎么说,是人就会犯错。为了防范运行时的错误,C++环境给予程序员试运行的机会,如果运行失败,它提供了一个预防机制。异常处理就是这种机制。异常是发生在运行期间的诸如零作分母、数组的溢出以及空闲空间的耗尽这样的错误事件。假如这些错误没有被捕获,则程序会突然地停止执行,而没有提供任何指示给使用者,告诉他发生了什么。处理这样的异常,不同的程序员有不同的风格,这导致了代码的多样性。这种多样性随着用户自定义类的使用而增长,因为每一种这样的类都带有其潜在的特定类的异常结构。当一个异常发生时,C++以下列方式做出反应:1.发生异常的函数可以产生一个系统定义的消息;8.1概述有时候,你可能面临并非所有的事情都是完美的事实8.1概述2.函数可以完全终止执行;3.函数可以跳过中间层继续执行其他的部分。

C++语言提供内建的对于意外情况处理的支持,这就是异常处理。通过C++语言的异常处理,你的程序能够使意外的事件与高层次的执行上下文进行通信,它能够从这种异常事件中恢复过来。

C++处理异常的机制是灵活的,因此它能处理任何类型的异常。在C++中,产生一个异常被称为引发一个异常。

8.1概述2.函数可以完全终止执行;

8.2何时使用异常处理

在程序运行期间调用一个函数会产生三种结果。它们是:1.正常执行在执行过程中,函数正常地执行并返回到调用的程序中.一些函数把结果代码返回给调用者。返回的代码表明执行的结果。2.错误执行当调用者在变量传递过程中犯错或调用的函数超出上下文的范围时,会导致一个错误,这会终止程序的执行。3.异常执行在执行过程中出现异常的一种典型情况是内存不足;另一种典型情况是一个I/O操作的异常。例如,当打开一个存在的文件时,存储媒介的设备驱动器可能会出错。这种情况不能通过简单的查找文件是否存在来处理,引发并捕捉异常是最佳的处理方法。

8.2何时使用异常处理在程序运行期间调用

8.2何时使用异常处理

在开发需要照顾意外情形的应用程序时,异常处理是必需的。发生在程序执行时的意外情形有:运行内存不够、资源分配错误、不能找到/打开文件等。假如这些错误没有被捕获,那么,在没有给予用户任何有关发生什么情况的提示下,程序将被中断。异常可以被定义为在程序执行中意外情况的出现和正常指令流的中断。

8.2何时使用异常处理在开发需要照顾意外8.3异常处理的基本语法

异常处理是C++的一个特征,它提供了处理运行期间异常的标准工具。一种一致性语法可以应用于这种目的,它能够被程序员很好的协调校正。一些常见的异常有分母为零、数组溢出以及NULL指针(即一个包含0值的指针)的引用。在C++语言中用try、throw和catch语句实现异常处理,在程序的某个函数中发现异常时使用throw表达式抛出这个异常,这个异常被抛给该函数的调用者,调用者用catch捕获该异常并进行相应的处理。当预测到某段程序将可能出现问题时,则该段程序放在try语句之后,使该段程序得以有机会抛出异常。8.3异常处理的基本语法异常处理是C++的8.3.1异常处理的语法

异常处理的语法如下:

throw表达式语法:

throw表达式

try表达式语法:

try

受保护的程序段语句

catch(异常类型)异常处理语句;

catch(异常类型)异常处理语句;

其中,catch括号中异常类型与throw抛出的异常类型相匹配,当某个异常发生后,catch语句将逐个被检查,直到某个异常类型与throw抛出的异常类型相匹配,然后执行catch后面的8.3.1异常处理的语法异常处理的语法如下8.3.1异常处理的语法异常处理程序。若catch括号中是省略号(……),则该catch可以捕获所有的异常类型,所以这样的语句一般放在所有catch语句的最后一个,否则其他catch语句就没有机会捕捉相应的错误类型了。当某个异常被捕捉并被相应的程序段处理后,系统将继续执行捕捉函数的其余部分。产生异常的函数和直到捕捉到异常的函数之间的所有调用链上的函数将从栈中删除。如果一个异常没有被任何函数捕捉到,则运行函数terminate将被调用,该函数的默认功能是调用abort函数终止程序的运行。一个异常处理的过程可以如下表示:

voidfun(){inti;doublef;

8.3.1异常处理的语法异常处理程序。若catch括号中是8.3.1异常处理的语法charc;…

//程序的其他部分

try

//预期可能出现问题的地方,保护起来

{throwi;//如果出现问题1,抛出一个整数(只是假设)

throwf;//如果出现问题2,抛出一个双精度数(只是假设)

throwc;//如果出现问题3,抛出一个字符(只是假设)

}

catch(intii){//相应处理

}catch(doubleff)

{

8.3.1异常处理的语法charc;8.3.1异常处理的语法//相应处理}catch(charcc)

{//相应处理}…//程序其他部分}【例8-1】#include<iostream.h>voidmain(){try{

8.3.1异常处理的语法//相应处理8.3.1异常处理的语法intage;cout<<”Entertheage:";cin>>age;if(age>100||age<1)throw"Invalidage!";cout<<"Afterthethrowstatement"<<endl;}catch(char*msg){cout<<"Error!"<<msg<<endl;}cout<<"Afterthecatchhandler"<<endl;}程序的输出结果为:8.3.1异常处理的语法intage;8.3.1异常处理的语法

Entertheage::46AfterthethrowstatementAfterthecatchhandleEntertheage::116Error!Invalidage!Afterthecatchhandle8.3.1异常处理的语法Entertheage::8.3.2异常的类型

异常有两种类型:同步异常和异步异常。1.同步异常发生在运行期间,并且能够使用throw表达式产生。仅有这种类型的异常能够使用C++中的异常处理器处理。控制不必返回异常引发处。2.异步异常因为一个键盘或鼠标的中断而产生。C++程序不能使用try和catch语句直接处理这种类型的异常。一次不得不产生一个自动调用与键盘或鼠标中断相关的例程。8.3.2异常的类型异常有两种类型:同步异常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语句在8.4Try、catch和throw语句5.如果找到了类型匹配的catch处理器,初始化它的参数并执行catch处理器中的指令。如果程序终止,释放堆栈中的所有值。一个基类的处理器也能够处理它的子类的异常。如果在catch表达式中遇到了一个“…”,它将处理所有的异常而不管它们的数据类型。

【例8-2】#include<iostream.h>voidmain(){cout<<"beforethethrowstatement"<<endl;try{throw25.6f;//引发一个浮点值}8.4Try、catch和throw语句5.如果找到了类型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语句8.4Try、catch和throw语句

在上面的代码中,一个浮点值从try模块中被引发。然而,没有为接受一个浮点值而特意设计的catch处理器。因此程序的输出是:beforethethrowstatementUnkonwndatatypeAfterthethrowstatement

新的运算符new引发了一个bad_alloc异常。新的头文件声明了bad_alloc类,它从异常类继承而来。What()函数在异常类中被声明。【例8-3】检查内存分配操作。#include<iostream.h>#include<new>voidmain()8.4Try、catch和throw语句在上8.4Try、catch和throw语句{char*buff;try{buff=newchar[1000000000];//声明在新的头文件中的新的运算符bad_alloc引发了一个类的对象

}catch(std::bad_alloc&obj){ cout<<":"<<obj.what()<<endl;}}

该程序是关于一个try模块和与它相联系的catch处理器的8.4Try、catch和throw语句{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.4Try、catch和throw语句的内建数据类型的数量。为了解决这个问题,一个用户定义类型的不同的对象能够引发给处理器。如下例:【例8-4】#include<iostream.h>#include<string.h>classMyException{public:charcause[21];MyException(char*in=0){strcpy(cause,in);}8.4Try、catch和throw语句的内建数据类型的数8.4Try、catch和throw语句voidprintMessage(){cout<<endl<<"Error:"<<cause<<endl;}};classAclass{intage;public:voidget(){cout<<"Enteryourage:";cin>>age;if(age<1||age>100)8.4Try、catch和throw语句voidprin8.4Try、catch和throw语句{MyExceptione("Problemwithage");throwe;}else{cout<<"CORRECTAGE!"<<endl;}}};voidmain(){Aclassobject;8.4Try、catch和throw语句{8.4Try、catch和throw语句try{ object.get();}catch(MyException&x){ x.printMessage();}}【例8-5】异常的抛出和捕捉在同一函数中。#include<iostream.h>#include<string.h>classPerson{8.4Try、catch和throw语句try8.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语句protected8.4Try、catch和throw语句throw"InvalidPerson'sname!";if((Old<0)||(Old>200))//有效性检查throwOld;if((Sex!='M')&&(Sex!='m')&&(Sex!='F')&&(Sex!='f'))//有效性检查

throwSex;}voidfun(){try{cout<<"funline1"<<endl;Personp1("zhang",43,1234566L,'M');//类Person的对象p1cout<<"funline2"<<endl;Personp2("",-1,22222222L,'F');//类Person的对象p2cout<<"funline3"<<endl;8.4Try、catch和throw语句throw"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语句Personp38.4Try、catch和throw语句{ cout<<"InvalidName!"<<endl;}}voidmain(){cout<<"beforefun..."<<endl;fun();cout<<"endfun..."<<endl;cout<<"inthemainfunction!"<<endl;}

在本程序中,异常的抛出和捕捉都在函数fun中,在实际应用中,异常的引发和处理一般在不同函数中,这样低层函数可以专注于具体问题,上层调用函数可以在恰当的位置设计对不同类型8.4Try、catch和throw语句{ 8.4Try、catch和throw语句的异常进行各种处理而不考虑低层函数是如何产生这个异常的。【例8-6】对【例8-5】稍加改动,使异常的抛出和捕捉在不同函数中进行。【例8-6】异常的抛出和捕捉在不同函数中。#include<iostream.h>#include<string.h>classPerson{protected:charName[20];//姓名intOld;//年龄unsignedlongID;//身份证号charSex;//性别8.4Try、catch和throw语句的异常进行各种处理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((Old<0)||(Old>200))//有效性检查throwOld;if((Sex!='M')&&(Sex!='m')&&(Sex!='F')&&(Sex!='f'))//有效性检查8.4Try、catch和throw语句public: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语句throwSe8.4Try、catch和throw语句voidmain(){try{cout<<”beforefun……”<<endl;fun();cout<<”endfun……”<<endl;}catch(char)//捕捉错误并进行处理{ cout<<"InvalidSex!"<<endl;}catch(int)//捕捉错误并进行处理{ 8.4Try、catch和throw语句voidmain8.4Try、catch和throw语句cout<<"InvalidOld!"<<endl;}catch(char*Msg)//捕捉错误并进行处理{ cout<<"InvalidName!"<<endl;}cout<<endl;cout<<"inthemainfunction!"<<endl;}下面这个例子演示了如何用异常处理纠正程序运行期错误。【例8-7】纠正如下程序运行期错误。#include<iostream.h>#include<string.h>classString8.4Try、catch和throw语句cout<<"In8.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语句{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语句{8.4Try、catch和throw语句};voidmain(){ StringstrObject;//该对象不包含任何东西

strObject.replace('+',''); strObject.display();}

上面的代码产生下面的运行期错误:

Segmentationfault(coredumped)

分析:要想纠正如上程序运行期错误,首先要找到出现异常的原因,然后采用异常处理机制去处理异常。因此,下面分两步进行介绍。1.标识异常的原因根据分析知道,在main()函数中,String类的一个空对象被8.4Try、catch和throw语句};8.4Try、catch和throw语句创建。replace()函数将遍历字符串直到空字符(‘\0’)出现,但如果字符串为空,那么操作失败。2.标识异常处理的机制采用try、throw、catch语句进行处理。修改上面程序得如下程序:#include<iostream.h>#include<String>classString{private: char*str;intcheck(){if(str==NULL)8.4Try、catch和throw语句创建。replac8.4Try、catch和throw语句return0;elsereturn1;}public:String(){str=0;}String(char*inString){str=newchar[strlen(inString)+1];strcpy(str,inString);}8.4Try、catch和throw语句return0;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语句voidrep8.4Try、catch和throw语句}voiddisplay(){cout<<str;}};voidmain(){StringstrObject;//对象不能包含任何的trytry{strObject.replace('+','');//引起replace函数异常strObject.display();}8.4Try、catch和throw语句}8.4Try、catch和throw语句catch(char*message){cout<<"Exception:"<<message<<endl;}}

程序的输出结果如下:Exception:NULLpointerexception8.4Try、catch和throw语句catch(cha8.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++库中的异常类标准C++库中8.5标准C++库中的异常类#include<iostream>#include<exception>usingnamespacestd;voidmain(){try{exceptiontheError;//声明一个C++标准异常类的对象throw(theError);//抛出该异常类的对象}catch(constexception&theError)//捕捉C++标准异常类的对象{cout<<theError.what()<<endl;//用what()成员函数显示出错原因}8.5标准C++库中的异常类#include<iostre8.5标准C++库中的异常类

try{logic_errortheLogicError("LogicError!");//声明一个C++标准异常类logic_error的对象throw(theLogicError);//抛出该异常类的对象}catch(constexception&theError)//捕捉C++标准异常类的对象{cout<<theError.what()<<endl;//用what()成员函数显示出错原因}}8.5标准C++库中的异常类

try8.6程序实例

【例8-9】设计一个异常Exception抽象类,在此基础上派生一个OutOfMemory类响应内存不足,派生一个RangeError类响应输入的数不在指定范围内,实现并测试这两个类。程序如下://EG8_8.CPP#include<iostream.h>classException{public: Exception(){}virtual~Exception()

8.6程序实例

【例8-9】设计一个异常Exception8.6程序实例

{}virtualvoidPrintError()=0;};classOutOfMemory:publicException{public:OutOfMemory(){}~OutOfMemory(){}8.6程序实例

{8.6程序实例

virtualvoidPrintError();};voidOutOfMemory::PrintError(){cout<<"OutofMemory!!"<<endl;}classRangError:publicException{public:RangError(unsignedlongnumber){BadNum=number;}8.6程序实例

virtualvoidPrintEr8.6程序实例

~RangError(){}virtualvoidPrintError();virtualunsignedlongGetNumber() { returnBadNum; }virtualvoidSetNumber(unsignedlongnumber) { BadNum=number; }8.6程序实例

~RangError()8.6程序实例

private:unsignedlongBadNum;};voidRangError::PrintError(){cout<<"NumberOutofrange.Youused"<<GetNumber()<<"!\n";}voidfn1();unsignedint*fn2();voidfn3(unsignedint*);voidmain(){try8.6程序实例

private:8.6程序实例

{fn1();}catch(Exception&theException){theException.PrintError();}}unsignedint*fn2(){unsignedint*n=newunsignedint;if(n==0)throwOutOfMemory();8.6程序实例

{8.6程序实例

returnn;}voidfn1(){unsignedint*p=fn2();fn3(p);cout<<"Thenumberis:"<<*p<<endl;deletep;}voidfn3(unsignedint*p){longNumber;cout<<"Enteraninteger(0---1000):";cin>>Number;8.6程序实例

returnn;8.6程序实例

if(Number>1000||Number<0)throwRangError(Number);*p=Number;}

程序的输出结果为:

Enteraninteger(0---1000):78Thenumberis:78Enteraninteger(0---1000):1500Numberoutofrange.Youused1500!下面给出一个接近实用的例子,假设开发一个人员管理系统,并且声明了人这个基本类Person,它包含姓名、性别、年龄、证件号码等数据,我们可以事先估计一下可能出现的异常情况,如用户忘记输入姓名,年龄不在正常范围内、证件号码不符合要求、性别不符合要求等等,这要求有一个可以报告这类错误8.6程序实例

if(Number>1000||Num8.6程序实例

的类InvalidPerson。同时,用户输入数据进行处理完成后需要存盘,下一次可以从磁盘中调出这些数据,这个过程中可能的异常是输入输出文件打开出错、输入数据被破坏等,要求有一个可以报告错误的类IOError。看下面的例子并体会异常处理的用法:【例8-10】人员管理系统中常用的异常处理。//EG8_9.H#ifndefEG8_9.H#defineEG8_9.Husingnamespacestd;classPerson;classError//声明出错类8.6程序实例

的类InvalidPerson。同时,用户8.6程序实例

{public:Error(constchar*theWhere,constchar*theWhy):why(theWhy),where(theWhere){}virtualvoiddisplay(ostream&out)const;//显示出错内容protected:conststringwhy;//错误原因conststringwhere;//错误地点};#endif//EG8_9.CPP#include<iostream.h>#include<string.h>8.6程序实例

{8.6程序实例

#include"#EG8_9.H"classInvalidPerson:publicError//从类Error派生类InvalidPerson{public:InvalidPerson(constPerson*thePerson,constchar*theWhy):Error("Person::InvalidCheck",theWhy),pPerson(thePerson){}//InvalidPerson类构造函数virtualvoiddisplay(ostream&out)const;//派生类中的出错内容显示protected:constPerson*pPerson;//派生类中新增数据成员};classIOError:publicError//从类Error派生类IOError8.6程序实例

#include"#EG8_9.H"8.6程序实例

{public:IOError(constchar*theWhere,constchar*theWhy,constchar*theFileName): //IOError构造函数Error(theWhere,theWhy),FileName(theFileName){}virtualvoiddisplay(ostream&out)const; //派生类中的出错内容显示protected:StringFileName; //文件名};classPerson{public:8.6程序实例

{8.6程序实例

Person(conststring&theName,inttheOld,unsignedlongtheId,chartheSex); //Person类构造函数voiddisplay(ostream&out)const;//类Person的显示函数voidoutput(ostream&out)const;//类Person的输出函数staticPerson*input(istream&in);//类Person的输出函数voidInvalidCheck()const;//类Person的有效性检查protected:stringName; //姓名intOld; //年龄unsignedlongId; //身份证号码charSex; //性别};ostream&operator<<(ostream&out,constPerson&thePerson);//重载<<运算符8.6程序实例

Person(conststring&8.6程序实例

voidError::display(ostream&out)const//Error::display函数的实现{out<<"!!!"<<"ErrorRepurt--"<<where<<":"<<why<<endl;//显示错误原因和地点}voidInvalidPerson::display(ostream&out)const//InvalidPerson::display函数的实现{Error::display(out);//调用基类的显示函数out<<"Personobject="<<(*pPerson)<<endl;//显示本类的新增特性}voidIOError::display(ostream&out)const//IOError::display函数的实现8.6程序实例

voidError::display(o8.6程序实例

{Error::display(out);//调用基类的显示函数out<<"File="<<FileName<<endl;//显示本类的新增特性}Person::Person(conststring&theName,inttheOld,unsignedlongtheId,chartheSex):Name(theName),Sex(theSex),Old(theOld),ID(theId){}//Person类构造函数voidPerson::InvalidCheck()const//Person::InvalidCheck函数的实现{if(Name.length()==0)//如果姓名为空,人员信息无效

{throwInvalidPerson(this,"Invalidperson'sname");8.6程序实例

{8.6程序实例

//抛出InvalidPerson}if(Old<0||Old>200)//如果人员年龄不在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程序实例

//抛出InvalidPerson8.6程序实例

{throwInvalidPerson(this,"Invalidperson'sSex");//抛出InvalidPerson}}voidPerson::display(ostream&out)const//Person::display函数的实现{out<<Name<<"-"<<Old<<"-"<<ID<<"-"<<Sex;//输出人员信息if(out.fail())//如果输出文件出错,则抛出IOError{throwIOError("Person::display"),"Outputfile","displayerror");}8.6程序实例

{8.6程序实例

}voidPerson::output(ostream&out)const//Person::output函数的实现{InvalidCheck();//首先进行有效性检查,该函数在对象无效时可以抛出InvalidPersonout<<"<"<<Name<<">"<<Old<<""<<ID<<""<<Sex<<"\n";//输出人员信息if(out.fail()) //如果输出文件出错,则抛出IOError{throwIOError("Person::output"),"Outputfile","outputerror");}}8.6程序实例

}8.6程序实例

voidPerson::input(istream&in)const//Person::input函数的实现{Person*TempPerson=0;//用于保存从in流中输入的对象stringTempPersonName;//保存从in流中输入的对象数据intTempPersonOld;unsignedlongTempPersonID;charTempPersonSex;try{if(in.get()!='<'//提取的数据不正确或者到了文件尾{if(in.eof())8.6程序实例

voidPerson::input(is8.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程序实例

{8.6程序实例

//用于输入数据构造临时对象TempPerson->InvalidCheck();//有效性检查catch(Error&theError)//捕捉错误进行初步处理{cout<<"Errorininput:"<<endl;theError.display(cout);//显示错误deleteTempPerson;//删除该临时对象TempPerson=(Person*)0;throwError("Person::input","inputerror");//抛出Error类的对象}returnTempPerson;}8.6程序实例

//用于输入数据构造临时对象8.6程序实例

//EG8_9.CPP#include<iostream.h>#include<fstream.h>#include"#EG8_9.H"ostream&operator<<(ostream&out,constPerson&thePerson);voidOutputPersons(char*theFileName);voidInputPersons(char*theFileName);voidmain(){try{OutputPerson("d:\\C++\\temp\\person.txt");//将类Person的若干个对象输出到文件中,可放于自己指定的目录下8.6程序实例

//EG8_9.CPP8.6程序实例

InputPerson("d:\\C++\\temp\\person.txt");//从文件中输入类Person对象并显示}catch(Error&theError)//捕捉错误类型,由于InvalidPerson和类IOError都是类Error的//子类,所以在此处也可以捕捉到这两种错误类型{cout<<"Fatalerrorinprogram:"<<endl;theError.display(cout);//错误信息显示}}ostream&operator<<(ostream&out,constPerson&thePerson);//重载<<运算符的实现{8.6程序实例

InputPerson("d:\\C++\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程序实例

thePerson.dispaly(out8.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程序实例

Personzhang("zhang",8.6程序实例

{throwIOError("InputPersons","can'topeninputfile",theFileName);}Person*TempPerson;while(!in.eof())//从文件中输入对象并显示该对象

{if(TempPerson=Person::input(in)){cout<<"PersonNo."<<++i<<":"<<(*TempPerson)<<endl;deleteTempPerson;}}}//源程序结束8.6程序实例

{第9章模板第9章模板

大多数人或多或少都看过或听说过印刷的过程。不管是印在布上还是在报纸上,通常要用到一个样本。印刷的一种方法是为每一页纸或每一米布一遍遍地重复制作样本。另一种更常见也是更广泛使用的方法是使用印版或模子。创建一个这种样本的模子或模板,用墨水或颜料对它染色,然后把这个样本印到所需要的媒质上。因此,做一个模子可以避免重复制作样本的麻烦。这个模子可以用来印刷任意多次。在C++中,模板就相当于这里说的模子。模板的实质是函数和类根据要求在运行时的实例化。这意味着程序员在编写代码时不必了解确切的应用环境。类的最终用户能够在执行的时候创建自定义的类。模板的概念可以应用于函数和类。模板是C++支持参数化多态的工具,使用模板可以使用户或者函数声明一种一般模式,使得类中的某些数据成员或者9.1概述大多数人或多或少都看过或听说过印刷的过程。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.1概述9.2.1函数模板和模板函数9.2.1函数模板和模板函数doublemax(doublea,doubleb){returna>b?a:b;}floatmax(floata,floatb){returna>b?a:b;}

可以看出,这样的四个函数的函数体都是相同的,其功能几乎完全一样,只是返回值或者参数不同。对不同的数据类型,相同的代码必须重写多次,而执行的是相同的功能。这是对时间和精力的浪费,为避免这种浪费,C++为我们提供了一个方便的语法规则:函数模板。利用函数模板,可以建立一个具有通用功能的函数,支持不同的函数参数和返回值。9.2.1函数模板和模板函数doublemax(doub

9.2.1函数模板和模板函数函数模板的语法形式如下:template<classT>…//函数定义…其中T代表在函数模板中要使用的通用类型,在该函数的调用过程中,T被具体化。例如对上面的重载函数,我们只要声明一个函数模板就可以了:template<classT>Tmax(Ta,Tb){returna>b?a:b}9.2.1函数模板和模板函数函数模板的语法形式如下:

9.2.1函数模板和模板函数

关键字template表明正在声明一个模板,数据类型参数T由模板参数<classT>给出。该模板的含义为:无论模板参数T实例为int、float、char或任意其他类型,包括类类型时,函数max()就为实例化了的参数求最大值。这样定义的max()函数在进行求最大值的操作时,首先必须将模板参数T实例化,从这个意义上说,这里定义的max()函数并不是一个完全的函数,我们称它为函数模板。因此,函数模板代表了一类函数,但是它不是一个完全的函数,必须将其模板参数T实例化后,才能完成具体函数的功能。将T实例化的参数常常称为模板实参。用模板实例化的函数称为模板函数,例如:voidfun(){inti;Mclassx,y;9.2.1函数模板和模板函数关键字tem

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函数模板和模板函数intj=max(i,

9.2.1函数模板和模板函数实例化实例化实例化函数模板max(Ta,Tb)模板函数max(inta,intb)模板函数max(doublea,doubleb)模板函数max(Xa,Xb)……图9-19.2.1函数模板和模板函数实例化实例化实例化函数模板

9.2.1函数模板和模板函数【例9-1】编写函数模板max()使得能找出任意类型的两个数中的最大数。#include<iostream.h>template<classtype>typemax(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函数模板和模板函数【例9-1】编写函数模板ma

9.2.1函数模板和模板函数程序的输出结果为:max('A','a'):amax(30,40):40max(45.67f,12.32f):45.67【例9-2】编写能完成计算任意类型数的平方的函数模板。#include<iostream.h>template<classtype>typesquare(typea){typeb;b=a*a;returnb;}voidmain()9.2.1函数模板和模板函数程序的输出结果为:

9.2.1函数模板和模板函数

{cout<<"square(23.45f):"<<square(23.45f)<<endl;cout<<"square(43):"<<square(43)<<endl;}程序的输出结果为:square(23.45f):549.903square(43):18499.2.1函数模板和模板函数{

9.2.2重载函数模板

在有些特殊情况下需要对函数模板进行重载,C++允许函数模板被一个或多个同名的非模板函数重载。考虑下面的例子:【例9-3】template<classT>Tmax(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重载函数模板在有些特殊情况下需

9.2.2重载函数模板

max(c,i);//错误}

这里出现的错误在于:模板类型并不知道int和char之间能进行隐式类型转换。但是,这样的转换在C++中是很普通的。为了解决这个问题,C++允许函数模板可以参与重载。用户可以用一个非模板函数重载一个同名的函数模板,例如,可以这样定义:template<classT>Tmax(Ta,Tb){return(a>b)?a:b;}intmax(int,int);//显式地声明函数max(int,int),不是模板函数9.2.2重载函数模板max(c,i);

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重载函数模板voidf(inti,cha

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++中,函数模板与同名的非模板函数的重载方法遵循下列约定:1.寻找一个参数完全匹配的函数,如果找到了,就调用它;2.寻找一个函数模板,将其实例化产生一个匹配的模板函数,如果找到了,就调用它;3.试一试低一级的对函数的重载方法,如通过类型转换可9.2.2重载函数模板charmax(char*x

9.2.2重载函数模板产生参数匹配等,如果找到了,就调用它。如果1、2、3均未找到匹配的函数,那么这个调用是一个错误。如果在第一步有多于一个的选择,那么这个调用是意义不明确的,也会产生一个错误。以上重载模板函数的规

温馨提示

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

评论

0/150

提交评论