第11章模板
【内容提要】 模板的概念
函数模板的定义和使用 类模板的定义和使用 【重点与难点】 11.1 模板的概念
模板是实现代码复用的一种工具,它可以实现类型参数化,把类型定义为参数,实现代码的真正复用。
模板分两类:函数模板和类模板。用一个代码段指定一组函数称为函数模板,或用一个代码段指定一组相关类称为类模版。 11.2 函数模板的定义和使用 11.2.1 函数模板的定义
格式为:
template
<返回类型><函数名>(参数表) { 函数体
}
说明:
①template是定义模板函数的关键字;template后面的尖括号不能省略;
②typename是声明数据类型参数标识符的关键字,也可用class。它用以说明其后面的标识符是数据类型标识符。在这个函数定义中,凡希望根据实参数据类型来确定数据类型的变量,都可以用数据类型参数标识符来说明,从而使这个变量可以适应不同的数据类型。
③定义函数模板时,可以声明多个类型参数标识符,各标识符之间用逗号分开。
④函数模板只是声明了一个函数的描述即模板,不是一个可以直接执行的函数,只有根据实际情况用实参的数据类型代替类型参数标识符之后,才能产生真正的函数。 11.2.2 模板函数
在使用函数模板时,要将形参“数据类型参数标识符”实例化为确定的数据类型。将类型形参实例化的参数称为模板实参,用模板实参实例化的函数称为模板函数。 11.2.3 重载模板函数
函数模板可使用多种方式重载。可以使用其它函数模板,指定不同参数的相同函数名。 也可以用非模板函数重载。
用非模板函数重载函数模板有两种方法:
① 借用函数模板的函数体,只声明非模板函数的原型,它的函数体借用函数模板的函
数体。
② 重新定义函数体。即重新定义一个完整的非模板函数,它所带的参数可以随意。 在C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则: 首先寻找一个参数完全匹配的函数,若找到就调用它。若找不到,则寻找一个函数模板,将其实例化生成一个匹配的模板函数,若找到就调用它。若找不到,则从第一步中通过类型转换产生参数匹配,若找到就调用它。否则调用失败。
11.3 类模板的定义和使用 11.3.1 类模板的定义
格式为:
template
//……
};
说明:
① template是声明类模板的关键字,template后面的尖括号不能省略。
② 定义类模板时,可以声明多个类型参数标识符,各标识符之间用逗号分开。
③ 类模板定义中,凡要采用标准数据类型的数据成员、成员函数的参数或返回类型的
前面都要加上类型标识符。
④ 如果类中的成员函数要在类的声明之外定义,则它必须是模板函数。其定义形式为:
template
函数返回类型类名<数据类型参数标识符>∷函数名(数据类型参数标识符形参1,??,数据类型参数标识符形参n) {
函数体 }
⑤ 类模板使类中的一些数据成员和成员函数的参数或返回值可以取任意的数据类型。
类模板不是一个具体的类,它代表着一族类,是这一族类的统一模式。使用类模板就是要将它实例化为具体的类。
11.3.2 模板类
将类模板的模板参数实例化后生成的具体的类,就是模板类。由类模板生成模板类的一般形式为:
类名<模板实参表>对象名1,对象名2,?,对象名n; 【典型例题】
例题1.有如下函数模板定义: template
T func(T x, T y) { return x*x*x+y*y*y; } 在下列对func的调用中,错误的是()。 (a) func(3, 5); (b) func(3.0, 5.5); (c) func (3, 5.5); (d) func
本题主要考查函数模板的使用方法。这里选项a是将函数模板中的类型形参实例化为int型,选项b是将函数模板中的类型形参实例化为double型。选项c中函数func的两个实参一个为int型,一个为double型,无法使用该函数模板。选项d中模板实参被显式指定,显示指定模板实参的方法是用尖括号<>将用逗号隔开的实参类型列表括起来紧跟在函数模板实例的名字后面。如选项d将实参类型指定为int型。本题答案为:c。
例题2.有如下程序: template
protected: int num T *p; public: Array(int); ~Array(); };
Array::Array(int x)// ① { num=x;// ② p=new T[num];}// ③ Array::~Array()//④ { delete []p;// ⑤ }
void main()
{ Array a(10);// ⑥ }
其中有错误的语句为__________________,应改正为_______________________________。 解答:
本题主要考查类模板的定义和使用。如果类中的成员函数要在类的声明之外定义,则它必须是模板函数。其定义形式为:
template
函数返回类型类名<数据类型参数标识符>∷函数名(数据类型参数标识符形参1,??,数据类型参数标识符形参n) {
函数体 }
本程序中类的构造函数和析构函数均在类中声明,类外定义。所以①、④语句关于这些函数的定义均错误,应遵循上边所述形式。由类模板生成模板类的一般形式为: 类名<模板实参表>对象名1,对象名2,?,对象名n; ⑥语句对类模板的使用错误。所以本题答案为:①、④、⑥ 改正程序:
①template
Array
例题3.运行下列程序结果为___________________________。 #include
template
char* add(char *a,char *b ) { return strcat(a,b); }
void main() {
int x=1,y=2;
double x1=1.1,y1=2.2; char p[10]=\ cout<
cout<
解答:
本题主要考查对函数模板的定义与使用以及重载模板函数的理解。 在C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则:
首先寻找一个参数完全匹配的函数,若找到就调用它。若找不到,则寻找一个函数模板,将其实例化生成一个匹配的模板函数,若找到就调用它。若找不到,则从第一步中通过类型转换产生参数匹配,若找到就调用它。否则调用失败。本题中主函数对函数add的四次调用中前三次都是未找到参数完全匹配的函数,于是找到一个函数模板,将该函数模板实例化为模板函数。第四次调用时找到了参数完全匹配的函数,于是调用了该函数。所以本题答案为:
3,3.3,3.1,C++ program。
例题4.下列关于类模板的模板参数说法正确的是()。
(a)只可作为数据成员的类型 (b)只可作为成员函数的参数类型 (c)只可作为成员函数的返回值类型 (d)以上三者都可以 解答:
类模板中的模板参数既可以作为类成员的类型,也可作为成员函数的参数类型还可作为成员函数的返回值类型。本题答案为:d。
例题5.有如下类模板的定义,请指出其中的错误___________________________。 template
class A {public: T num;} 解答:
类模板的定义还是在定义类,所以类定义结束的分号不能少。答案为:定义语句的最后缺少一个分号。
例题6.下列说法正确的是()。
(a) 如果从一个带单个static数据成员的类模板产生几个模板类,则每个模板类共享类模板static数据成员的一个副本。
(b) 模板函数可以用同名的另一个模板函数重载。 (c) 同一个形参名只能用于一个模板函数。
(d) 关键字class指定函数模板类型参数,实际上表示“任何用户自定义类型”。 解答:
在非模板类中,类的所有对象共享一个static数据成员。从类模板实例化的每个模板类有自己的类模板static数据成员,该模板类的所有对象共享一个static数据成员。每个模板类有自己的类模板的static数据成员副本。每个模板类有自己的静态数据成员副本。所以选项a错误。同一形式参数名可以用于多个模板函数。选项c错误。关键字class指定函数模板的类型参数,实际上表示“任何内部类型或用户自定义类型”。选项d错误。答案为:b
例题7.下面是3个数字求和的类模板程序。请将程序补充完整。 #include
template
void main() { _______________ s1(1,2,3); //定义类对象 cout< 解答: 本题主要考查对类模板的应用。答案为:int size,array[0]+array[1]+array[2], sum 例题8.定义一个求幂函数的函数模板。 解答: 参考程序: #include { T ans = a; while(--exp>0) ans*=a; return ans; } int main() { cout << \ cout << \ return 0; } 【习题】 一、选择题 1.模板对类型的参数化提供了很好的支持,因此()。 (a) 类模板的主要作用是生成抽象类 (b) 类模板实例化时,编辑器将根据给出的模板实参生成一个类(c) 在类模板中的数据成员都具有同样类型 (d) 模板中的成员函数都没有返回值 2.有如下模板: template 下列对模板函数fun的调用中错误的是()。 (a) a 3.关于关键字class和typename,下列表述中正确的是()。 (a)程序中的typename都可以替换为class (b)程序中的class都可以替换为typename (c)在模板形参表中只能用typename来声明参数的类型 (d)在模板形参表中只能用class或typename来声明参数的类型 4.运行下列程序的结果为()。 #include void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; cout<<\} int main(void) { int i=1, j=2; double x=1.1, y=2.2; char a='x', b='z'; swap(i,j); (d) a(97);