第第9章章 模板模板主要内容主要内容•知识点1: 模板定义•知识点2: 函数模板与模板函数•知识点3: 类模板与模板类知识点知识点1: 模板定义模板定义•所谓模板,就是将某段程序中的数据类型参数化,所谓模板,就是将某段程序中的数据类型参数化,使得它能够处理某个范围内的数据类型使得它能够处理某个范围内的数据类型. .而不必为每而不必为每种可能的类型都建立一个实例,从而避免了重复劳种可能的类型都建立一个实例,从而避免了重复劳动,增强了程序的灵活性和有效性动,增强了程序的灵活性和有效性•模板具有模板具有形式类属参数形式类属参数形式类属参数形式类属参数:将数据类型作为模板的参:将数据类型作为模板的参数,参数化的数据类型就是形式类属参数数,参数化的数据类型就是形式类属参数•模板须经过模板须经过实例化实例化实例化实例化后才能使用后才能使用:实例化是指用某一:实例化是指用某一具体数据类型替代模板中的形式类属参数的过程,具体数据类型替代模板中的形式类属参数的过程,该确定的数据类型称为该确定的数据类型称为实际类属参数实际类属参数实际类属参数实际类属参数知识点知识点2: 函数模板与模板函数函数模板与模板函数•基本思想:–实际应用中,一些函数的功能相同,唯一的区实际应用中,一些函数的功能相同,唯一的区别只在于处理对象的数据类型不同,若用函数别只在于处理对象的数据类型不同,若用函数重载实现,则需编写多个函数:重载实现,则需编写多个函数:例:例:例:例:intint max(intmax(int i, i, intint j) j) float float max(floatmax(float i, float j) i, float j) { return i>j ? { return i>j ? i:ji:j; }; }{ return i>j ? { return i>j ? i:ji:j; }; }–编程时只提供一套实现该功能的程序实体,然编程时只提供一套实现该功能的程序实体,然后将数据类型作为参数传递后将数据类型作为参数传递,这就是模板的思,这就是模板的思想想Ø对不同数据类型的操作完全相同,用函数模对不同数据类型的操作完全相同,用函数模板实现更为简洁方便板实现更为简洁方便template < 类型形式参数表类型形式参数表 >类型类型 函数名函数名 (( 形式参数表形式参数表 )) { 语句序列语句序列 }函数模板声明函数模板声明Ø 函数模板定义由模板说明和函数定义组成函数模板定义由模板说明和函数定义组成Ø 模板说明的类属参数必须在函数定义中至少出现一次模板说明的类属参数必须在函数定义中至少出现一次Ø 函数参数表中可以使用类属类型参数,也可以使用一般类型函数参数表中可以使用类属类型参数,也可以使用一般类型参数参数 template < 类型形式参数表类型形式参数表 > 类型形式参数的形式为:typename T1 , typename T2 , …… , typename Tn 或class T1 , class T2 , …… , class Tn •例例1: 1:•#include •template < typename T >•T max ( T a , T b )•{ return a > b ? a : b ; }•void main ( )•{ cout << " max ( 3 , 5 ) is " << max ( 3 , 5 ) << endl ;• cout << " max ( 'y' , 'e' ) is " << max ( 'y' , 'e' ) << endl ;• cout << " max ( 9.3 , 0.5 ) is " << max ( 9.3 , 0.5 ) << endl ;•}•例2:冒泡排序模板•template •void SortBubble ( ElementType *a , int size )•{ int i, work ;• ElementType temp ;• for (int pass = 1; pass < size; pass ++ )• { work = 1;• for ( i = 0; i < size-pass; i ++ )• if ( a[i] > a[i+1] )• { temp = a[i] ; a[i] = a[i+1] ; • a[i+1] = temp ; work = 0 ; }• if ( work ) break ;• }•} •#include <#include >•void main()void main()•{ {•intint a[5]={45,20,30,100,5}; a[5]={45,20,30,100,5};•SortBubble(a,5);SortBubble(a,5);•for(intfor(int i=0;i<5;i++) i=0;i<5;i++)•coutcout<<<TYPE max(TYPE x, TYPE y){ return (x>=y) ? x:y; } template TYPE max(TYPE x, TYPE y, TYPE z){ TYPE w = (x>=y)?x:y ;return (w>=z)?w:z; }template template TYPE TYPE max(TYPEmax(TYPE x[], x[], intint n) n){ { TYPE m = x[0];TYPE m = x[0];for (for (intint i=0; i
否则,的实际参数类型匹配,则调用该函数否则,•(2)(2)如如果果能能从从从从同同同同名名名名的的的的类类类类属属属属函函函函数数数数实实实实例例例例化化化化一一一一个个个个函函函函数数数数实实实实例例例例,,而而该该函函数数的的参参数数类类型型正正好好与与函函数数调调用用的的实实际际参参数数类类型匹配,则调用该实例化的函数否则,型匹配,则调用该实例化的函数否则,•(3)(3)对对函函数数调调用用的的实实际际参参数数作作隐隐式式类类型型转转换换后后与与非非非非类类类类属属属属函函函函数数数数再再作作匹匹配配,,找找到到匹匹配配的的函函数数则则调调用用它它否否则,则,•(4)(4)提示语法错误提示语法错误•练习:P369 9.2 第1题•P370 9.4 第1题知识点知识点3: 类模板与模板类类模板与模板类•类模板的定义:类模板的定义:•一般形式:一般形式:template >class class 类名类名类名类名 { {…………}; }; – –形式类属参数表形式类属参数表形式类属参数表形式类属参数表:用尖括号括起来的部分。
用逗号隔开:用尖括号括起来的部分用逗号隔开:用尖括号括起来的部分用逗号隔开:用尖括号括起来的部分用逗号隔开不同的形式类属参数,每个类属参数都由不同的形式类属参数,每个类属参数都由不同的形式类属参数,每个类属参数都由不同的形式类属参数,每个类属参数都由classclass引入引入引入引入– –类属参数在类的声明中有效,在类外无效类属参数在类的声明中有效,在类外无效类属参数在类的声明中有效,在类外无效类属参数在类的声明中有效,在类外无效– –在类的定义中,把这些类属参数当数据类型来声明各种在类的定义中,把这些类属参数当数据类型来声明各种在类的定义中,把这些类属参数当数据类型来声明各种在类的定义中,把这些类属参数当数据类型来声明各种变量变量变量变量例:数组类模板例:数组类模板•template< typename T >•class Array•{ public :• Array ( int s ) ;• virtual ~ Array () ;• virtual const T& Entry( int index ) const ;• virtual void Enter( int index, const T & value ) ;• protected : • int size ;• T * element ;•} ;•template Array::Array(int s)• { if ( s > 1 ) size = s ; else size = 1 ;• element = new T [ size ] ; }•template < typename T > Array < T > :: ~Array()• { delete [] element ; }•template < typename T > const T& Array < T > :: Entry ( int index ) const• { return element [ index ] ; }•template < typename T > void Array < T > :: Enter(int index, const T& value)• { element [ index ] = value ; }•void main()•{•Array a(5);•a.Enter(2,10);•cout< 例:例:STACKSTACK类模板,不能用于创建对象类模板,不能用于创建对象STACK > objobj; ;实例化:用实际类属参数实例化:用实际类属参数intint替替换形式类属参数换形式类属参数ELEMENT_TYPEELEMENT_TYPE后,得到一个整形堆栈后,得到一个整形堆栈类,即可用于声明对象。
类,即可用于声明对象•一个类模板可以实例化为多个不同的一个类模板可以实例化为多个不同的类类•#include •#include "Array.h"•void main()•{ Array IntAry( 5 ) ;• int i ; • for ( i = 0; i < 5; i ++ ) IntAry.Enter ( i, i ) ;• cout << "Integer Array : \n" ;• for ( i = 0; i < 5; i ++ ) cout << IntAry.Entry(i) << '\t' ;• cout< DouAry( 5 ) ;• for ( i = 0; i < 5; i ++ ) DouAry.Enter ( i, (i+1)*0.35 ) ;• cout << "Double Array : \n" ;• for ( i = 0; i < 5; i ++ ) cout << DouAry.Entry(i) << '\t' ;• cout< > // // 声明一个类属类作为基类。
声明一个类属类作为基类template template class BASE {class BASE {public:public:void void show(TYPEshow(TYPE objobj) ){ {coutcout << << objobj << "\n"; << "\n";return; return; } }}; }; // 声明一个类属类作为BASE的派生类template class DERIVED: public BASE { public: void show2(TYPE1 obj1, TYPE2 obj2){cout << obj1 << " " << obj2 << "\n";return; }};•例 9-6#includetemplate< typename T >//定义类模板定义类模板class A{ public : A( T x ) { t = x ; } void out() { cout << t << endl ; } protected : T t ;} ;class B: public A > //派生一般类派生一般类{ public : B ( int a, double x ) : A > ( a ) { y = x ; } void out() { A > :: out() ; cout << y << endl ; } protected : double y ;};void main(){ A a( 123 ) ; a.out() ; B b ( 789, 5.16 ) ; b.out() ;}实例化基类实例化基类抽象类型参数抽象类型参数•自己学习P 363页 9.5 名空间。