1、1,1.,函数模板,l,现象:某些重载函数的功能相同,仅参数类型不同。,如:,void swap(,int,& x,int,& y),int,t=x;x=y;y=t; ,void swap(float ,void swap(double,l,问题:若能对重载函数的参数类型做抽象表达,即用统一,形式表达,则可大大简化源程序。,l,解法:定义函数模板。,template,void swap(,T,&x,T,&y),T,t=x;x=y;y=t; ,l,template,:,关键字,用于定义模板。,l,一对尖括号:界定模板的参数表。,l,class(,或,typename,),:,关键字,用于,定义抽
2、象类型,T(,可用其它标识符,),。,l,其后,可用抽象类型,T,定义变量。,2,3,4,5,函数模板的使用,l,传递给函数模板的数据类型也可以是自定义数据类型。,#,include,using namespace std;,class Point, /,自定义,类,Point,int,x,y;,public:,Point(,int,a,int,b) x=a;y=b; ,void show() std:,cout,(x,y),n; ,;,template /,定义函数模板,void swap(T ,int,main(void), Point p1(6,9),p2(12,23);,swap(p1
3、,p2);,p1.show(); p2.show();,return 0;,6,函数模板的重载与特例,l,例,函数模板的重载,#,include,template,/,求两个任意类型值中的较大者,T max(T x,T y), return xy?x:y; ,template,/,求任意类型数组中的最大元素,T max(T* x,int,n=10), T m=x0;,for(,int,i=1;in;i+) if(mxi) m=xi;,return m;,int,main(void),int,data10=76,89,65,90,97,78,75,62,99,54;,std:,cout,max(
4、88.6,53.8),max(data),n;,return 0;,7,8,9,10,11,12,13,14,类模板的使用,l,例,栈类模板的应用实例。,#,include,using namespace std;,template/,模板也可有默认参数,class Stack,T stackI;,int,top;,public:,Stack( ) top=0; ,void Push(T item), if(top=I),cout,Stack is full.,n; return; ,stacktop+=item;,T Pop( );,;,15,类模板的使用,template,T Stack:
5、Pop( ), if(top=0),cout,Stack underflow.,n; return 0; ,top,-,;,return stacktop;,int,main(void),Stack,iStack,;,/,定义整型栈对象,大小为默认值,5,iStack,.Push(1);,iStack,.Push(2);,cout,iStack,.Pop( ),endl,;,cout,iStack,.Pop( ),endl,;,cout,iStack,.Pop( ),endl,;,return 0;,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
6、,抛出异常(throw)小结: 如果程序发生异常情况,我们可以将出错信息throw出去。 所抛出的异常信息,可以使用任意类型的变量作为出错信息的标识,便于处理者根据这个标识来专心处理错误。更为常用的是抛出一个专门用来处理错误的类的对象,使用者只要捕获该对象,就可以通过对象的服务处理异常。务完成错误处理工作。,class A/专门处理异常情况的类public:A()cout3) throw A();coutx must 3!endl;void main()try /sos(4);/抛出异常 sos(2);/未抛出异常catch(A x)x.showError();, 此时关键字 throw 首先
7、调用被抛出类的构造函数创建一个原执行程序中并不存在的对象。其次这个对象成为该函数在发生异常时的函数返回值,即这个对象的类型不是函数设计的正常返回类型。,35,例,定义并使用异常类,#,include,#include,using namespace std;,class,StackExpt,/,定义异常类,char message30;,public:,StackExpt,(char*,msg,),strcpy,(message,msg,); ,const char* what()const return message; ,;,class Stack,int,*,elem,size,top;
8、,public:,Stack(,int,n),elem,=new,int,n;size=n;top=0; ,Stack() delete,elem,; ,void Push(,int,item), if(top=size),throw,StackExpt,(Stack is full.),;/,抛掷异常对象,elem,top+=item;,l,抛掷的异常除了允许基本类型的数据外,,也允许自定义类型的数据。,36,37,38,39,40,41,指定函数抛掷的异常类型,l,若用,throw,指定函数可能抛掷的异常类型,则应避免,向函数外,抛,掷非指定的异常类型,否则将导致程序异常终止。例如:,#,
9、include,using namespace std;,void f(,int,n) throw(,int,), try,if(n=0) throw zero;/,对:可在函数内被捕获,if(n0),throw 3.5,;/,错:,不可向函数外抛出,double,型异常,catch(char*),cout,error; ,int,main(), try,f(,-,1),; ,catch(,int,t),cout,t,endl,; ,return 0;,l,运行时,导致程序异常终止。,42,43,44,45,46,47,C+标准异常类 C+类标准库定义了异常类可以用于我们自己的程序中。假若标准
10、异常类不能满足需要,我们可以从这些类中派生并添加自己的特定内容。下面的三张表描述了这些标准异常类:,其中异常基类 exception 的定义如下:class _CRTIMP exceptionpublic: exception(); exception(const _exString它的派生类,以 logic_error 为例,其定义如下:,class _CRTIMP logic_error : public exception public:explicit logic_error(const string,在程序设计中使用异常机制应该注意:1. 注意使用异常规格说明 异常规格说明像一个函数
11、原型,它告诉用户书写异常处理 代码以及处理什麽异常,告诉编译器异常可能出现在这个 函数中。 有时这个特定函数所调用的函数产生了一个出乎意料的异 常,有时一个不会抛出异常的老函数被一个会抛出异常的 新函数替换了,这将产生对 unexpected() 的调用。无论何 时,只要使用异常规格说明或者调用含有异常的函数,都 应该创建自己的 unexpected() 函数,该函数记录信息而且 重新抛出同样的异常。2. 尽量使用标准异常类 在创建自己的异常前应检查标准C+异常库。假若标准异 常正合所需,则会使用户更易于理解和处理。,#include using namespace std;void my_u
12、nexpected_handler() throw 1;void f() throw(int) throw 1L; / oops - *bad* function int main() set_unexpected(my_unexpected_handler); try f(); catch (.) return 0; ,例:创建自己的 unexpected() 函数,标准C+异常是一个异常层次的优秀范例,通过使用可进一步增强和丰富它。3. 用引用而非值去捕获 如果抛出一个派生类对象而且该对象被基类的异常处理器通过值捕获到,对象会被“切片”,也就是说,随着向基类对象的传递,派生类元素会被依次割
13、下,直到传递完成。要尽力避免这种情况,因为这时对象的行为像基类而不像它本来应该是的派生类对象。,4. 在构造函数中抛出异常 由于构造函数没有返回值,因此,如果没有异常机制,可以按以下两个选择用于报告在构造期间的错误: (1) 设置一个非局部的标志并希望用户检查它。 (2) 返回一个不完全被创建的对象并希望用户检查它。,5. 不要在析构函数中导致异常 由于析构函数会在抛出其他异常时被调用,所以永远不要打算在析构函数中抛出一个异常或者通过执行在析构函数中相同的动作导致其他异常的抛出。如果这些发生了,这意味着在已存在的异常到达引起捕获之前抛出一个新的异常,这会导致对 terminate() 的调用。
14、 这就是说:假若调用一个析构函数中的任何函数都有可能会抛出异常,则这些调用应该写在析构函数中的一个 try 块中,而且析构函数必须自己处理所有自身的异常,即这里的异常都不应逃离析构函数。6. 避免无保护的指针 假若资源分配给无保护的指针,那麽意味着在构造函数中存在一个缺点。由于该指针不拥有析构函数,所以当在构造函数中抛出异常时那些资源将不能被释放。,开销 使用任何一个新特性必然有所开销。当异常被抛出时有相当的运行时间方面的开销,这就是从来不打算把异常用于普通程序流控制的一部分原因。异常的发生应当是很少的,所以开销集中在异常上而不是在普通的执行代码上。设计异常处理的重要目标之一是:在异常处理实现中,当异常不发生时应不影响运行速度。这就是说,只要不抛出异常,代码的运行速度如同没有加载异常处理时一样,这是因为异常处理都依赖于使用特定的编译器。 异常处理也会引出额外信息(空间开销),这些信息被编译器置于栈上。 异常对象除了能作为特定的异常范围的对象传进送出外,也可以像其他对象一样被正确地在周围传送。当异常处理器工作完成时,异常对象也被相应地销毁。,