C/C++程序设计
1
模板是 C++语言在 20世纪 80年代末引进的。原先模板视为对包容类如链表和向量类的有力支撑,进来更跃升为标准模板库 (standard template library
or STL)通用程序设计的厚实基石,模板语法表面虽然变幻多端,但其内涵是非常简单的,模板是编译器许可 (complier-approved) 的宏,这些强大的宏中包括一些数据类型名作为替换参数 。
C/C++程序设计
2
一、模板的概念和方法二、函数模板三、函数模板和函数重载算例四、非模板的重载函数五、类模板六、类模板的默认参数
C/C++程序设计
3
一、模板的概念和方法许多算法表现了本质的雷同性,如数组的排序计算,对于 short型数据需要编写一套排序插入删除的算法,对于
double型又要重复相似的过程,当然知其一很容易变化到其二。
有鉴于此 C++允许类型作为变动的要素,简化程序的手工切换工作。
模板将数据类型作为参数出现在通用的算法描述中,作为参数的类名简称为类参,类参的个数没有限制,类参的命名遵循标识符的规定。类参既可替换为内置的数据类型如
float,int等,也可替换为类类型名、结构名和联合名。
C/C++程序设计
4
基于一般的惯例类参的名称常写为 type1,type2,...typen
或 T1,T2,...,TN。类参在模板的描述中出现的位置也就是原来具体类名如 long,short,类类型名 CType等所在的位置。
类名之后空格分隔的名称理解为内存的标识名或形参。
模板表现了源代码的直接替换,变动的基本因素是量度数据大小的尺度即数据类型。
设 type为类参,type替换为 short对应 short变量的数据处理,type替换为 long则专门应付 long型的数据流动。
如此通用程序设计方法油然而生。
C/C++程序设计
5
模板首先得进行声明。
模板的声明中包含带参的类名,这些类名的实在化在其后的阶段才得以实施。
因此对于一个带参的模板声明,编译器能扫描到有限的错误,在特定类参具体的实现点,编译器会捕获更多的错误,直到反复调试错误减至最少。
代码重用存在许多途径,模板的代码重用机制是 C语言宏展开的自然发展。
宏展开的一套文本替换算法从预处理阶段搬移到编译阶段,结合函数重载中的一套类型匹配搜寻算法一起就诞生了模板的内在运作机制,这是编译软件人员代码的重用。
C/C++程序设计
6
模板表现了源代码的直接替换,变动的基本因素是量度数据大小的尺度即数据类型。设 type为类参,type替换为
short对应 short变量的数据处理,type替换为 long则专门应付 long型的数据流动。如此通用程序设计方法油然而生。
模板首先得进行声明。模板的声明中包含带参的类名,
这些类名的实在化在其后的阶段才得以实施。
代码重用存在许多途径,模板的代码重用机制是 C语言宏展开的自然发展。宏展开的一套文本替换算法从预处理阶段搬移到编译阶段,结合函数重载中的一套类型匹配搜寻算法一起就诞生了模板的内在运作机制,这是编译软件人员代码的重用。
C/C++程序设计
7
二、函数模板函数模板是关于通用函数的数据类型作为可变参量的算法描述。
1,函数模板的机制函数模板的机制介于函数重载和带参的宏定义之间,函数模板和宏一样只有一个描述,宏定义是描述的就地扩展,
函数模板尚需进行类似于函数重载涉及的类型安全匹配较检,不同的实参的类型对应函数模板诞生不同的版本。每一单独的函数版本称为函数模板的具体实现,一个函数模板潜在对应多个具体实现。
C/C++程序设计
8
2,函数模板的声明函数模板的声明通过关键字 template开始,该关键字之后是尖括号 <>括起来的带类型参量的列表或模板类参表,
每一类参前放置关键字 class或 typename修饰,说明随后的名称是类名参量,至少存在一个类参。
新添关键字 typename专门引进来减轻 class的负担,但
class由于键入的字符少历史悠久,一直是模板格式的主流用法。本课件以 class为主阐述模板的格式,简单地以两个类参为例进行说明如下:
template < class T1,typename T2 >
ret_type function (T1 v1,long a,T2 v2,..,)
{ 函数模板的定义体语句 ; }
C/C++程序设计
9
对于函数模板,类参 T1,T2必须至少在函数模板名
fuction的形参列表中出现一次。
函数模板的声明不导致代码的实际产生,但为代码的产生提供统一的模式。模板声明具有全局作用范围。
不要将尖括号中的类参表中的类型参数和函数名后圆括号形参列表中形参混淆,形参是运行时压入堆栈传递给函数的数据值,而类参是在函数调用点获得的实参的静态数据类型,数据类型是静态的,该数据类型在编译阶段确定。
编译器扫描表达式的上下环境,可以在编译时反演出实参的数据类型。
C/C++程序设计
10
3,函数模板的实现函数模板是一种描述,在调用点产生具体的代码称为模板函数。模板函数的实现根据类似于宏展开的代码生成机制,
可潜在地对应多个重载函数。一旦模板生成确定的重载函数,
编译器根据函数重载的原理进行细节的调用匹配。
在进行了上面的函数模板声明之后,就可以像带参宏定义或其它函数一样实施模板函数的调用。调用的格式取决于类参在函数模板的具体含义,其形式为:
function(e1,longExpre,e2,..,);
e1,e2是表达式,表达式的数据类型确定了函数模板的具体实现。
C/C++程序设计
11
如果 e1的数据类型为 int,e2的数据类型为 double,这两个数据类型如果匹配函数模板的类型要求,则编译器在内部产生下面的代码:
ret_type function(int v1,long a,double v2,..,)
{ 模板函数的定义体语句 ; }
可用指定的类名去初始化模板类参表中的类参,这样便于形成用户期望的函数版本,以增强程序的清晰性。类参初始表应与相应的实参和模板类参表匹配。其格式为:
function<long,double>(e1,longExpre,e2,..,);
C/C++程序设计
12
三、函数模板和函数重载算例
[例 ] 函数模板和函数重载
#include <stdio.h>
template <class T>
void swap (T x,T y)
{ T temp;
temp=x;x=y;y=temp;
switch (sizeof(T))
{ default,printf ("T=TYPE;");
case sizeof (long):printf ("T=long;"); break;
case sizeof (double):printf ("T=double;"); break;
}
}
C/C++程序设计
13
void main()
{ long a(10),b(20);
swap (a,b);
double d=30.66;
swap<double>(d,b);
}
//输出,T=long; T=double;
C/C++程序设计
14
四、非模板的重载函数根据模板生成的函数多于一个时形成若干重载函数,有时希望特殊地提供一个同名的函数,由此形成非模板的重载函数,这也称为函数模板的特殊化。
如此定制的函数对于模板的扩展起着抑制作用,模板函数只存在唯一的细分版本,程序员提供了该版本,编译器就采用此版本而不再节外生枝。
C/C++程序设计
15
[例 ] 模板函数和定制的同名函数
#include <stdio.h>
template < class T1,typename T2>
void Show(T1 v1,T2 v2)
{ printf ("sizeof(T1)=%d,sizeof (T2)=%d\n",
sizeof (T1),sizeof (T2)); }
void Show (float,long)
{ printf ("float=%d,long=%d\n",sizeof (float),
sizeof (long)); }
C/C++程序设计
16
class B
{ public,B(long k=1){ x=k;} long x; float y; };
void main()
{ B b;
Show (b.y,b.x); Show (2.0f,b); Show(1.0,'c');
}
程序运行输出结果如下:
float=4,long=4
sizeof(T1)=4,sizeof(T2)=8
sizeof(T1)=8,sizeof(T2)=1
C/C++程序设计
17
从上面结果可以看出编译器生成唯一的重载函数版本
Show (float,long),这就是定制的重载函数。
模板的重载函数双引号括起来的类参名如同宏定义一样不在替换的文本之列
Show (2.0f,b)
生成 Show (float,class B )
因为常数 2.0f拥有数据类型 float。
Show (1.0,'c')生成函数版本 Show (double,char) 。
C/C++程序设计
18
五、类模板类模板是在原先具备一定功能的类上松动若干数据类型而演化出来的,类模板是类的数据结构和算法中许可数据类型作为可变因素的通用描述。
可变动的数据类型称为类参,类模板中的类参获得具体可行的类名称为类模板的具体实现。
类模板的具体实现是一个切实可行的类,称为特定类或实在类或模板类。
类模板的使用格式分下面三个步骤:
C/C++程序设计
19
1,类模板的声明类模板的声明格式不失一般性以两个类参 T1,T2和一个
int型的形参 nMax来表示:
template < class T1,class T2,int nMax>
class CType
{ public,T1 m_t1Data;
T2 m_t2Data[nMax];
CType ();
~CType ();
T1 funct (T1 v1);
typedef CType<T1,T2,nMax> CTypeN_T;
};
C/C++程序设计
20
template <class T1,class T2,int nMax >与函数模板的引导格式是类似的,尖括号之间是模板类参形参表。
模板类参形参表允许具体类名如 int带形参如 nMax,类参由 class或 typename引入,至少存在一个类参。形参由具体的类名引入,通过逗号分隔。
类参 T1,T2的具体值等待类名初始化,形参 nMax用整数初始化。 class引入类的声明,类的声明通过用户指定的类名 CType来代表,CType作为类模板的构造函数名和析构函数名的核心部分,同时也作为类模板的类域分辨符的关键前导。
本课件将 template <class T1,class T2,int nMax >简称为模板带参。
C/C++程序设计
21
2,类模板成员函数的描述
template <class T1,class T2,int nMax>
CType<T1,T2,nMax>::CType(){}
template <class T1,class T2,int nMax>
CType<T1,T2,nMax>::~CType(){}
template <class T1,class T2,int nMax>
T1 CType<T1,T2,nMax>::funct(T1 v1) {return v1;}
C/C++程序设计
22
类模板的成员函数起始于 template,结束于函数体的右花括号。
类模板的成员函数描述首先用模板带参引导,表示其后的成员函数隶属于一个抽象的类模板。
CType<T1,T2,nMax>是类模板的通用类名,
CType<T1,T2,nMax>::是类模板变通的类域分辨符,类模板潜在对应的类族由这个通用的分辨符进一步细化,类参
T1,T2形参 nMax 明确时就是变通的类域分辨符固定之时。
CType类名之后即括号括起来的内容是将模板类参表中的 class或 typename和 int去掉得来的,次序个数保持不变。
C/C++程序设计
23
CType<T1,T2,nMax >::funct可视为全限定成员函数名,因此紧密地结合在一块,前面冠以函数的返回类型,后面跟圆括号包括的形参列表。
注意类模板变通的类域分辨符后的构造函数名和析构函数名中并没有包括模板参数表 <T1,T2,nMax>,C++语言的设计者明显感到重复此参数表是多余的。
在类模板内可以声明一个类模板的通用类的别名如下:
typedef CType<T1,T2,nMax> CTypeN_T;
CTypeN_T可以在类域中采用。
C/C++程序设计
24
3,类模板的具体实现上面的两个步骤引进了两个类参 T1,T2和一个给数组成员定界的维数 nMax,由此形成通用的类其类名为
CType<T1,T2,nMax >,其中的类参取具体的类名 t1,t2,形参取具体数值 max时触发特定类的诞生。其一般格式为:
CType<t1,t2,max> object;
具体地有:
CType<long,long,50> a;
CType<float,long,20> b;
类模板对象定义语句 [CType<float,long,20> b;]激发两个相关的事件:
C/C++程序设计
25
CType< float,long,20>导致类模板产生一个名称为
CType< float,long,20>的特定的类这个特定的类是通过类参 T1替换为 float,T2替换为 long,
nMax 替换为 20进行内部的宏展开形成的,不妨认为展开的结果具有如下形式:
class CType<float,long,20>
{ public,float m_t1Data;
long m_t2Data[20];
CType(); ~CType();
float funct (float v1);
};
C/C++程序设计
26
CType<float,long,20>::CType(){}
CType<float,long,20>::~CType(){}
float CType<float,long,20>::funct (float v1) {return v1;}
上面的展开格式称为类模板的定制,如果程序员提交了如此特定类的定制声明格式,编译器就不再根据类模板生成如此类的第二个版本,这就像函数模板的情形,用户基于模板或类似于模板的手工实现优于编译器的机械复制。
总的原则是编译器保证只存在一个类参独立的版本,以确保全局类名和函数名的内部唯一性。编译器在扩展时会进行适当的名称细分,然后重新扫描成员函数的具体代码。如果这个过程得以通过则进一步:
C/C++程序设计
27
调用这个特定类的构造函数,从而诞生一个该类的对象 b
同理 [CType<long,long,50> a;]触发相似的类参的宏替换,成员函数代码的重新审核。
由于这种内部扩展的机制过于曲折迂回,有些明显的概念错误可能逃过编译器的检查。
因此在建立类模板的时候,最好事先进行特定类的具体编程。
然后再选择若干类名作为类型参量,进行特定类到通用类的升级处理。
C/C++程序设计
28
4,类模板的成员函数显式实现上面产生模板特定类的方法称为模板类的对象定义法,
采用此方法根据类模板产生一个特定类的同时尚诞生特定类的对象。
类模板的成员函数通常只在该函数被调用时,才生成特定的代码。可以不提供成员函数的代码,但一旦调用成员函数该代码必须存在。虚函数的地址需要进入虚表,因此需提供虚函数的模板描述。
模板的显式实现语法可以产生类模板的特定类而无须此类的对象,或者成员函数的一个特定实现而无须程序实际调用这个函数。
C/C++程序设计
29
下面的语法格式将类模板的成员函数全部生成一个完整的代码:
template class CType<float,long,18>;
或针对一个模板类指定生成一个成员函数:
template float CType<float,long,8>::funct (float v1);
如果这个特定类存在多个实例或特定的成员函数多次调用,采用此这种显式的实现手段是比较安全的。
C/C++程序设计
30
[例 ] 两个类参的类模板
#include <iostream.h>
template < class T1,class T2 >
class CType
{ public,T1 m_t1Data; T2 m_t2Data;
CType(T1 v1,T2 v2);
void Show();
};
template <class T1,class T2 >
CType<T1,T2>::CType(T1 v1,T2 v2)
{ m_t1Data=v1; m_t2Data=v2; }
C/C++程序设计
31
template <class T1,class T2 >
inline void CType<T1,T2>::Show()
{ cout<<m_t1Data<<"--"<<m_t2Data<<endl; }
void main()
{
{ CType<char,long> obj('c','c');
obj.Show();}
{ CType<long,char*> obj('C',"c is letter");
obj.Show(); }
}
C/C++程序设计
32
六、类模板的默认参数类模板尖括号中的类参或形参可提供默认值,类参的默认值是类名,形参的默认是一个具体的整型常数。 可以通过明显的格式忽略默认值的设置。
template < class T1,class T2 =int>
class CType
{ public,T1 m_t1Data;
T2 m_t2Data;
CType(T1 v1,T2 v2);
};
C/C++程序设计
33
对于这个类模板的声明,如下的定义:
CType<long> obj('C',1);
等价于明确的形式,CType<long,int> obj('C',1);
明显的定义格式,CType<long,double> x(1,1.0);忽略默认值的设置。类模板形参中的整形参量也可以指定一个默认值,便于多次定义该默认值生成特定类的对象时简化代码的书写量。 如:
template < class T,int nMax =20>
class CArray
{ T m_ptData [nMax] ;
public,CArray (T nElement) ;
};
因此定义语句,[CArray<char> c;]
同等于 [CArray<char,20> c;]
C/C++程序设计
34
小结函数模板和类模板提供了类型参数化的通用机制。函数模板的类参在函数的调用点根据实参的类型反演出来,形成重载函数,同时实参的数值作为入口又进行函数调用。
类模板的类参一般由对象定义的方式显式给出,根据直接指定的类名或整型常数初始化模板类参形参表中的对应项目值,用这些具体的类名和常数替换模板中的类参或形参,
形成特定的类。
这些工作是编译器自动宏替换复制完成的,但比预处理的宏替换执行了更多的类型安全检查。根据模板生成的代码具有明显的相近性质。
C/C++程序设计
35
函数模板产生的函数是一系列形参个数相同形参数据类型不同的重载函数,类模板产生的类是类名不同结构相近的类。
类模板的不同实现是不同的类,此特定类上的模板成员函数不同于彼模板类相对应的成员函数版本。
其间通过类域分辨符进行了清晰的分界,因此类模板提供一种类型安全的鉴别机制。
C/C++程序设计
36