C++程序设计第6章(1)━━函数模板、类模板主要内容●关于模板●函数模板的定义●函数模板实例化●关于类模板●类模板的定义●类模板实例化●顺序表类模板的定义关于模板关于模版:①为提高程序的通用性,必须使其代码不受数据类型的限制,即建立与数据类型无关的通用函数或通用类,方法是将其中的数据类型参数化,在函数或类的定义中引入数据类型参数。
②模板是函数或类的通用样板,当函数或类需要处理多种不同类型数据时,可通过模板来创建具有通用功能的函数或类,即函数模板或类模板,以达到通用的目的。
③由于一个函数模板可以支持多个具有不同数据类型形参的函数,从而简化了重载函数的设计。
④C++国际标准ISO 14882 将模板正式引入标准库,以模板类取代传统的C++类。
⑤程序中的软件模块由模板构造,即函数模板实例化产生相应的模板函数,类模板实例化产生相应的模板类,这种程序设计类型称为参数化程序设计。
函数模板的定义函数模板的定义:template <模板形参列表> 返回值类型函数名(函数形参列表){ 函数体}①模板形参有两种:类型参数、常规参数②类型参数:由typename或class后加一个标识符构成,该标识符用在紧跟其后的函数中代表一种潜在的数据类型。
③常规参数:由类型符( 如int、float ) 后加一个标识符构成,该标识符用在紧跟其后的函数中代表一个潜在的常量。
④模板形参格式:typename 形参名class 形参名数据类型形参名①函数的返回值类型、形参类型以及函数体中,均可使用前面模板形参表中给出的类型参数来代表某种潜在的数据类型,该类型参数代表的具体类型只有在调用函数时根据给出的模板实参才能确定!②在函数体中,还可使用前面模板形参表中给出的常规参数来代表一个潜在的常量,该常规参数代表的具体值只有在调用函数时根据给出的模板实参才能确定!因此其对应的模板实参必须是常量表达式。
【例】(四个求绝对值的重载函数)#include<iostream.h>int abs ( int x ){return ( x>0 ? x : -x ) ; } long abs ( long x ){return ( x>0 ? x : -x ) ; } float abs ( float x ){return ( x>0 ? x : -x ) ; } double abs ( double x ) {return ( x>0 ? x : -x ) ; } void main ( ){ int a = -96 ; long b = 78 ;float c = -3.6 ; double d = 5.8 ; cout << “|a|=” << abs (a)<< endl ; cout << “|b|=” << abs (b)<< endl ; cout << “|c|=” << abs (c)<< endl ; cout << “|d|=” << abs (d)<< endl ; }(求绝对值的函数模板)#include<iostream.h>template < typename T>T abs ( T x ){return ( x>0 ? x : -x ) ; }void main ( ){ int a = -96 ; long b = 78 ;float c = -3.6 ; double d = 5.8 ;cout<< “|a|=” << abs<int>(a)<<endl ; cout<< “|b|=” << abs<long>(b)<<endl ; cout<< “|c|=” << abs<float>(c)<<endl ; cout<< “|d|=” << abs<double>(d)<<endl ; }函数模板函数重载指具有相同函数名,但根据不同参数确定不同入口,解决不同问题。
若这些同名函数只是在“返回值类型”或“参数类型”上不同,可通过一个通用的函数模板,替代多个具有不同类型形参的函数。
运行:| a | = 96| b | = 78| c | = 3.6| d | = 5.8●函数模板实例化:①编译系统生成函数模板的某个具体函数版本的过程称为函数模板实例化,每一个实例就是一个模板函数。
②编译系统根据每一次函数调用时所(显式或隐式)给出的类型实参生成相应的函数调用式,并生成相应的函数版本,即模板函数。
在实例化过程中,用类型实参替代函数模板中相应的类型形参。
调用式中显式给出模板实参●模板函数的显式调用:函数名< 模板实参列表>(函数实参列表)【例】上例中的四次调用:cout << “|a|=” << abs< int >( a )<< endl ;cout << “|b|=” << abs< long >( b )<< endl ;cout << “|c|=” <<abs< float >( c )<< endl ;cout << “|d|=” << abs< double >( d )<< endl ;模板函数的隐式调用:函数名(函数实参列表)㈠模板实参可以有条件的省略。
当调用函数时,若模板实参表中最右边的若干个实参,其代表的类型信息可从函数实参表中获得,则相应的模板实参可省略,编译将自动补足省略的模板实参。
㈡模板实参不能省略的情况:①从函数实参表中获得的类型信息有矛盾。
②类型形参没有出现在函数形参表中。
省略调用式中没有给出模板实参【例】#include <iostream.h>template < typename T > T abs( T x ) {return ( x>0 ? x : -x ) ; }void main ( ){ int a = -96 ; long b = 78 ; float c = -3.6 ; double d = 5.8 ;cout << “|a|=” << abs ( a )<< endl ;cout << “|b|=” << abs ( b )<< endl ;cout << “|c|=” << abs ( c )<< endl ;cout << “|d|=” << abs ( d )<< endl ; }运行:| a | = 96| b | = 78| c | = 3.6| d | = 5.8【例】(求数组元素中的最大值的函数模板、调用时隐式或显式给出模板实参)#include <iostream.h>template < typename T >T max ( T *p , int n ){ T max = p[ 0 ] ; for ( int i=1 ; i<n ; i++ ) if ( max < p[ i ] ) max = p[ i ] ;return max ; }void main( ){int a[ 10 ] = { 3 , 56 , 7 , -7 , -45 , 32 , 1 , 64 , 99 , 72 } ;double b[ 6 ] = { 32.4 , 99.8 , -6.5 , -78.4 , 65.4 , 2345 } ;int ta = max ( a , 10 ); //或max < int > ( a , 10 ) ;cout << “整数数组中的最大值=” << ta << endl ;double tb = max ( b , 6 ); //或max < double > ( b , 6 ) ;cout<< “实数数组中的最大值=” << tb << endl ; }运行:整数数组中的最大值= 99实数数组中的最大值= 2345函数模板实例化过程分析:①max ( a , 10 ) ;系统根据函数实参表中数组a 的类型为int ,推出此次调用T 代表int 。
②max ( b, 6 ) ;系统根据函数实参表中数组b 的类型为double ,推出此次调用T 代表double 。
③max < int > ( a , 10 ) ;显式给出模板实参为int ,此次调用T 代表int 。
④max < double > ( b , 6 ) ;显式给出模板实参为double ,此次调用T 代表double 。
【例】(两矩阵相乘函数模板、调用时显式给出模板实参)分析:a 为m ×n 矩阵,b 为n ×p 矩阵,a 乘以b 得到c 矩阵,则c 为m ×p 的矩阵。
k=n-1矩阵相乘公式: c ij = ∑ a ik × b kjk=0# include<iostream.h># include<iomanip.h>template < typename T1 , typename T2 , typename T3 > void multi ( T1 *pa , T2 *pb , T3 *pc , int m , int n , int p ){ int i , j , k ;for ( i=0 ; i<m ; i++ )for ( j=0 ; j<p ; j++ ){ pc[ i ][ j ]= 0 ;for ( k=0 ; k<n ; k++ ) pc[ i ][ j ]+= pa[ i ][ k ]* pb[ k ][ j ]; }}由此可见pa 、pb 、pc 为行指针!template < typename T >void print ( T *p , int m , int n ){ int i , j ;for (i=0 ;i<m;i++ ){for ( j=0 ; j<n ; j++ ) cout << setw( 6 ) << p[ i ][ j ] ;cout << endl ; }}void main ( ){ int a[ 2 ][ 3 ] = { { 1, 2, 3 } , { 4, 5, 6 } } ;float b[ 3 ][ 5 ] = { { 1.2, 2.1, 3.2, 4, 5 } , { 6, 7, 8, 9, 6 } , { 5, 4, 3, 2, 1 } } ; double c[ 2 ][ 5 ] ;multi < int [ 3 ], float [ 5 ], double [ 5 ]>( a , b , c , 2 , 3 , 5 ) ;cout << “矩阵a:\n” ;print< >( a , 2 , 3 ) ;//省略模板实参cout << “矩阵b:\n” ;print ( b , 3 , 5 ) ;//省略模板实参cout << “矩阵c:\n” ;print< double [ 5 ]>( c , 2 , 5 ) ;}运行:矩阵a : 1 2 34 5 6矩阵b : 1.2 2.1 3.2 4 56 7 8 9 65 4 3 2 1矩阵c :28.2 28.1 28.2 28 2064.8 67.4 70.8 73 56由此可见p 为行指针!关于类模板关于类模板:①类模板是一系列相关类的模型或样板,这些类的成员组成相同,成员函数的源代码形式也相同,所不同的只是所针对的数据类型,包括成员数据的类型、成员函数的参数类型和返回值类型。