Download presentation
Presentation is loading. Please wait.
1
面向对象程序设计 QQ群: Object-Oriented Programming 汽车学院
2
面向对象的程序设计 本章主要内容: 模板概念 函数模板与模板函数 类模板与模板类 异常处理 异常处理概述 异常处理方法 2/21
3
第6章 模板与异常处理 6.1 模板的概念 6.2 函数模板与模板函数 6.3 类模板与模板类 6.4 异常处理 6.5 应用举例
4
6.1 模板的概念 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1. 面向对象的思想:继承和多态
2. 模板机制
5
如果能使这些函数只写一遍,即写一个通用的函数,适用于不同数据类型,便会大大提高代码的可重用性。C++的模板可以解决这一问题。
6.1 模板的概念 int max(int x,int y) { return (x>y)?x:y;} long max(long x,long y) double max(double x,double y) char max(char x, char y) 这些函数函数体都一样,但由于所处理的参数类型和返回值不同,所以是完全不同的函数。虽然可以通过重载使它们有相同的函数名,但还是得为每个函数写一组代码。 如果能使这些函数只写一遍,即写一个通用的函数,适用于不同数据类型,便会大大提高代码的可重用性。C++的模板可以解决这一问题。
6
6.1 模板的概念 若一个程序的功能是对某种特定的数据类型进行处理,则可以将所处理的数据类型说明为参数,以便在其他数据类型的情况下使用,这就是模板的由来。 模板是以一种完全通用的方法来设计函数或类而不必预先说明将被使用的每个对象的类型。 通过模板可以产生类或函数的集合,使它们操作不同的数据类型,从而避免需要为每一种数据类型产生一个单独的类或函数。
7
6.1 模板的概念 模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码重用。 模板分为函数模板和类模板,它们分别允许用户构造模板函数和模板类。
8
6.1 模板的概念 模板分类 函数模板(function template) 是独立于类型的函数 可产生函数的特定版本 类模板(class template) 跟类相关的模板 可产生类对特定类型的版本
9
模板 (函数模板和类模板) 模板函数 模板类 对象
6.1 模板的概念 模板 (函数模板和类模板) 实例化 实例化 模板函数 模板类 实例化 对象
10
第6章 模板与异常处理 6.1 模板的概念 6.2 函数模板与模板函数 6.3 类模板与模板类 6.4 异常处理 6.5 应用举例
11
6.2 函数模板与模板函数 函数模板的说明 建立一个通用函数,其函数返回类型与形参类型不具体指定,由虚拟类型来代表。这个通用函数就称为函数模板。 函数模板的一般说明形式如下: template <typeName 类型参数>或 template <class 类型参数> 返回类型 函数名(模板形参表) { 函数体 } 关键字template: 表示正在声明一个模板 关键字typename: 表示其后的参数是一个虚拟的类型名 类型参数(通常用T,Type等):虚拟的类型名
12
6.2 函数模板与模板函数 例如:将求最大值函数max()定义成函数模板,如下所示:
template < typename T> T max(T x,T y) { return (x>y)?x:y; } 也可以定义成如下形式: template < class T> 其中,T为类型参数,它既可取系统预定义的数据类型,又可以取用户自定义的类型。
13
6.2 函数模板与模板函数 使用函数模板时,关键字typename(或class)后面的类型参数必须用实际的数据类型替代它。
当编译系统发现有一个函数调用: 函数名(模板实参表); 函数模板经实例化生成的具体函数叫模板函数。该模板函数的函数体与函数模板的函数定义体相同。 函数模板代表一类函数,模板函数表示某一具体函数
14
6.2 函数模板与模板函数 例6.1 函数模板的使用。//p261 #include <iostream>
using namespace std; template < typename AT> AT max(AT x,AT y) { return (x>y) ? x:y; } int main() { int i1= 10, i2=50; double d1=50.344, d2= ; char c1='k ',c2='n'; cout<<"The max of i1, i2 is: "<<max(i1,i2) <<endl; cout<<"The max of d1, d2 is: "<<max(d1,d2) <<endl; cout<<"The max of c1, c2 is: "<<max(c1,c2) <<endl; return 0; } AT:类型参数 模板声明,定义函数模板 模板实参
15
6.2 函数模板与模板函数 例6.2 有关指针的函数模板 //p263 using namespace std;
#include <iostream> using namespace std; template <typename T> T sum(T *array, int size=0) { T total=0; for (int i=0; i<size; i++) total+=array[i]; return total; } int int_array[ ]={1,2,3,4,5,6,7,8,9 ,10}; double double_array[ ]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10}; int main() { int itotal=sum(int_array, 10) ; double dtotal=sum(double_array, 10) ; cout<<"itotal=" <<itotal <<endl; cout<<"dtotal=" <<dtotal <<endl; return 0; }
16
6.2 函数模板与模板函数 说 明 在template语句与函数模板定义语句之间不能有别的语句。
说 明 在template语句与函数模板定义语句之间不能有别的语句。 template <class T> int i; // 错误 T max(T x,T y) { return (x>y)?x:y; } 模板函数类似于重载函数, 只不过它更严格一些。在函数重载时,每个函数体可以执行不同的动作。但同一函数模板实例化后的所有模板函数都必须执行相同的动作。 在函数模板中允许使用多个类型参数, 每个类型参数前必须有关键字class/typename。
17
6.2 函数模板与模板函数 #include<iostream> using namespace std;
template<typename type1, typename type2> void myfunc(type1 x, type2 y) { cout<<x<<" "<<y<<endl; } int main() myfunc(10, "hao"); myfunc(0.123, 10L); return 0;
18
6.2 函数模板与模板函数 4. 同一般函数一样,函数模板也可以重载. 定义2个类型参数的函数模板max 定义3个类型参数的函数模板max
4. 同一般函数一样,函数模板也可以重载. 例6.4:#include<iostream.h> //p264 template<typename type> type max(type x, type y) { return (x>y)?x:y; } type max(type x, type y , type z) { type t; t= (x>y)?x:y ; return (t>z)?t:z ; } int main() { int m=10,n=20,max2; double a=10.1,b=20.2,c=30.3,max3; max2=max(m,n); max3=max(a,b,c); cout<<"max("<<m<<","<<n<<")="<<max2<<endl; cout<<"max("<<a<<","<<b<<","<<c<<")=" <<max3<<endl; return 0;} 定义2个类型参数的函数模板max 定义3个类型参数的函数模板max 调用模板函数max
19
6.2 函数模板与模板函数 函数模板与同名的非模板函数可以重载. 例6.5:
函数模板与同名的非模板函数可以重载. 例6.5: #include<iostream.h> //p265 template<typename AT> AT max(AT x, AT y) { cout<<“调用模板函数:”; return (x>y)?x:y; } int max(int x, int y) { cout<<“调用非模板函数:”; return (x>y)?x:y; } int main() { int i1=10,i2=20; double d1=10.1,d2=20.2; char c1=‘k’,c2=‘n’; cout<<"较小的整数是:"<<max(i1,i2)<<endl; cout<<"较小的双精度数是:"<<max(d1,d2)<<endl; cout<<"较小的字符串是:"<<max(c1,c2)<<endl; return 0; }
20
6.2 函数模板与模板函数 在C++中, 函数模板和同名的非模板函数重载时,调用的顺序遵循以下约定:
1) 寻找一个参数完全匹配的函数, 如果有则调用之。 2) 寻找一个函数模板, 将其实例化,产生一个匹配的模板函数, 若匹配则调用之。 3) 若1步和2步都失败, 就再试低一级的对函数调用方法,例如通过类型转换可产生参数匹配等。 若找到了则调用之。 4) 若1步2步3步都未找到匹配的参数,则返回一个错误的调用。 5) 若在1步有多于一个的选择, 即函数调用存在二义性,则也是错误的调用。
21
第6章 模板与异常处理 6.1 模板的概念 6.2 函数模板与模板函数 6.3 类模板与模板类 6.4 异常处理 6.5 应用举例
22
6.3 类模板与模板类 类模板:建立一个通用类,其数据成员、成员函数的返回类型和形参类型不具体指定,用一个虚拟的类型来代替。 使用类模板定义对象时,系统根据实参的类型来取代类模板中虚拟类型从而实现不同的功能。
23
定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,然后是类名,其格式如下:
6.3 类模板与模板类 定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,然后是类名,其格式如下: template <typename[或class] 类型参数> class 类名 { //… }; 模板参数 类型参数 如Type <类型参数>与函数模板中的意思相同,是一个虚拟类型名 类成员的声明方法同普通类的定义,只是各个成员(数据和函数)可以用模板的通用类型参数
24
在类定义中, 欲采用通用数据类型的数据成员, 成员函数的参数或返回值前面需要加上Type。
6.3 类模板与模板类 const int size=10; template <class Type> class stack { public: void init(){ tos=0; } void push (Type ch); Type pop( ); private: Type stck[size]; int tos; }; 在类定义中, 欲采用通用数据类型的数据成员, 成员函数的参数或返回值前面需要加上Type。
25
在类体外定义有类型参数的成员函数时, 需要在函数体外进行模板声明, 并且在函数名前的类名后加上“<Type>”
6.3 类模板与模板类 在类体外定义有类型参数的成员函数时, 需要在函数体外进行模板声明, 并且在函数名前的类名后加上“<Type>” template <class Type> void stack<Type>::push(Type ob) { if (tos==size) { cout<<"stack is full "; return; } stck[tos]=ob; tos++;
26
在类体外定义有类型参数的成员函数时, 需要在函数体外进行模板声明, 并且在函数名前的类名后加上“<Type>”
6.3 类模板与模板类 在类体外定义有类型参数的成员函数时, 需要在函数体外进行模板声明, 并且在函数名前的类名后加上“<Type>” template <class Type> Type stack<Type>::pop( ) { if (tos==0) { cout<<"stack is empty "; return 0; } tos - -; return stck[tos];
27
6.3 类模板与模板类 类模板不是代表一个具体的、实际的类, 而是代表着一类类。实际上, 类模板的使用就是将类模板实例化成一个具体的类, 它的格式为: 类模板名 <实际的类型> 对象名; 例: stack <char> s1, s2;
28
6.3 类模板与模板类 例6.8 使用栈类模板 //p269 #include<iostream>
using namespace std; const int size=10; template<class Type> //声明一个类模板 class stack{ public: void init(){ tos=0; } void push(Type ch); //参数取Type类型 Type pop(); //返回类型取Type类型 private: Type stck[size]; //数组的类型为类型参数Type, //即可取任意类型 int tos; }; template <class Type> void stack<Type>::push(Type ob) { // }
29
6.3 类模板与模板类 template <class Type> Type stack <Type>::pop()
{ // } int main() { //定义字符堆栈 stack <char> s; //创建模板参数为char型的对象 int i; s.init(); s.push('a'); s.push('b'); s.push('c'); for(i=0;i<3;i++) cout<<"pop s: "<<s.pop()<<endl;
30
6.3 类模板与模板类 //定义整型堆栈 stack <int> is; //创建模板参数为int型的对象 is.init();
is.push(1); is.push(3); is.push(5); for (i=0;i<3;i++) cout<<"pop is: "<<is.pop()<<endl; return 0; } #include<iostream> using namespace std; const int size=10; template<class Type> //声明一个类模板 class stack{ public: void init(){ tos=0; } void push(Type ch); //参数取Type类型 Type pop(); //返回类型取Type类型 private: Type stck[size]; //数组的类型为类型参数Type, 即可取任意类型 int tos; }; template <class Type> void stack<Type>::push(Type ob) { if(tos==size) cout<<"Stack is full"; return; } stck[tos]=ob; tos++; Type stack <Type>::pop() if(tos==0) {cout<<"Stack is empty"; return 0;} tos--; return stck[tos]; int main() { //定义字符堆栈 stack <char> s; //创建模板参数为char型的对象 int i; s.init(); s.push('a'); s.push('b'); s.push('c'); for(i=0;i<3;i++) cout<<"pop s: "<<s.pop()<<endl; //定义整型堆栈 stack <int> is; //创建模板参数为int型的对象 is.init(); is.push(1); is.push(3); is.push(5); for (i=0;i<3;i++) cout<<"pop is: "<<is.pop()<<endl; return 0;
31
6.3 类模板与模板类 类模板 stack <Type> 模板类 stack <char>
stack <int> stack <double> 实例化 对象s 对象is 对象ds
32
6.3 类模板与模板类 说明 在每个类模板定义之前, 都需要在前面加上模板声明: template <class Type>
class stack { 类模板在使用时, 必须在类名后面缀上模板参数<Type>: stack <Type> s1, s2; 模板类可以有多个类型参数.
33
6.3 类模板与模板类 例6.9 使用两个类型参数的类模板 //p272 #include<iostream>
using namespace std; template<class T1, class T2> class myclass{ public: myclass (T1 a, T2 b){i=a; j=b;} void show() {cout<<"i="<<i<<"j="<<j<<endl;} private: T1 i; T2 j; }; int main ( ) { myclass <int, double> ob1(12, 0.15); myclass <char,char*> ob2('x',"Test"); ob1.show(); ob2.show(); return 0; } #include<iostream> using namespace std; template<class T1, class T2> class myclass{ public: myclass (T1 a, T2 b){i=a; j=b;} void show() {cout<<"i="<<i<<"j="<<j<<endl;} private: T1 i; T2 j; }; int main ( ) { myclass <int, double> ob1(12, 0.15); myclass <char,char*> ob2('x',"Test"); ob1.show(); ob2.show(); return 0; }
34
第6章 模板与异常处理 6.1 模板的概念 6.2 函数模板与模板函数 6.3 类模板与模板类 6.4 异常处理 6.5 应用举例
35
6.4 异常处理 6.4.1 异常处理概述 6.4.2 异常处理的方法
36
6.4.1 异常处理概述 1、常见的错误分为两大类: 1)编译时的错误 例如:关键字拼写错误、语句末尾缺分号、括 号不匹配等。
例如:关键字拼写错误、语句末尾缺分号、括 号不匹配等。 这类错误相对容易修正。 2)运行时的错误 例如:无法打开输出文件、数组下标越界等。 这类错误较隐蔽,不易被发现。 2、运行过程中出现的错误统称为异常,对异常的处理称为异常处理。传统异常的处理方法基本上是采取判断语句或分支语句来实现。
37
6.4.1 异常处理概述 例6.10 传统的异常处理方法举例.P273 #include<iostream>
using namespace std; int Div(int x, int y); int main() { cout<<"7/3 = "<<Div(7, 3)<<endl; cout<<"5/0 = "<<Div(5, 0)<<endl; return 0; } int Div(int x, int y) if(y == 0) cout<<"除数为0,错误!"<<endl; exit(0); return x/y;
38
6.4 异常处理 6.4.1 异常处理概述 6.4.2 异常处理的方法
39
6.4.2 异常处理的方法 C++异常处理办法: 如果在执行一个函数过程中出现异常,可以不在本函数中立即处理,而是发出一个信息,传给它的上一级(即调用函数)来解决,如果上一级函数也不能处理,就再传给其上一级处理。如此逐级上传,如果到最高一层还无法处理,运行系统一般会自动调用系统函数terminate,用它调用abort终止函数。
40
6.4.2 异常处理的方法 C++异常处理办法: C++处理异常的机制由检查、抛出和捕获3个部分组成,分别由3种语句来完成:try(检查)、throw(抛出)和catch(捕获)。
41
6.4.2 异常处理的方法 1、异常的抛出 抛出异常格式: throw 表达式; 例如:
int Div(int x, int y) { if(y == 0) throw y; //抛出异常,当除数为0时,语句throw将抛出int型异常 return x/y; } 若某段程序出现异常,可用throw抛出异常给调用者,由与之匹配的catch捕获。throw中的表达式表示抛出的异常类型,由表达式的类型来表示。
42
6.4.2 异常处理的方法 2、异常的检测和捕获 检测和捕获的格式: try{被检查的复合语句;} catch(异常类型声明1)
{进行异常处理的复合语句1} catch(异常类型声明2) {进行异常处理的复合语句2} . catch(异常类型声明n) {进行异常处理的复合语句n}
43
6.4.2 异常处理的方法 例6.11 处理除数为0的异常的程序. P276 #include<iostream>
using namespace std; int Div(int x, int y); int main() { try{ cout<<"7/3 = "<<Div(7, 3)<<endl; cout<<"5/0 = "<<Div(5, 0)<<endl; } catch(int){ cout<<"除数为0,错误!"<<endl; } return 0; int Div(int x, int y) if(y == 0) throw y; return x/y;
44
6.4.2 异常处理的方法 说明 被检测的语句或程序段必须放在try中,否则不起作用。
try和catch块中的花括号必须有,即使括号内只有一条语句也不能省。 一个try_catch结构中只能有一个try块,但可以有多个catch块,以便于不同的异常信息匹配。catch后面括号中一般只写异常信息的类型名。
45
6.4.2 异常处理的方法 例6.12 有多个catch块的异常处理程序 #include<iostream>
using namespace std; int main() { double a = 2.5; try{ throw a; } catch (int ) {cout<<"异常发生!整数型!"<<endl;} catch (double ) {cout<<"异常发生!双精度型!"<<endl;} cout<<"end"<<endl; return 0; }
46
6.4.2 异常处理的方法 4.如果在catch子句中没有指定异常信息的类型,而用了三点删节号“...”,则表示它可以捕获任何类型的异常信息。 例6.13 有“...”删节号的异常处理程序 #include<iostream> using namespace std; int main() { double a = 2.5; try{ throw a; } catch (int ) {cout<<"异常发生!整数型!"<<endl;} catch (...) {cout<<"任意类型异常!"<<endl;} cout<<"end"<<endl; return 0; }
47
6.4.2 异常处理的方法 5.在某种情况下,在throw语句中可以不包括表达式,如: throw;
6.C++中,一旦抛出一个异常,而程序又不捕获的话,那么系统就会调用一个系统函数terminate,由它调用abort终止程序。
48
第6章 模板与异常处理 6.1 模板的概念 6.2 函数模板与模板函数 6.3 类模板与模板类 6.4 异常处理 6.5 应用举例
49
6.5 应用举例 自学:P 。
50
Practice, Practice, and Practice
Q & A Practice, Practice, and Practice Object-Oriented Programming 汽车学院
Similar presentations