34.c/c++程序员面试宝典-模板
在现代的c++技术中,模板技术是一种比较新的技术,同时也是一种极为有用的技术。在新的标准c++类库中,例如STL和Boost等都采用的是模板技术。
通过使用模板技术,可以使代码得到最大限度的重用。这是基于一种类型参数化的技术实现的。从而对于一些针对不同的类型有相同的操作的算法,能够通过模板技术的使用实现代码的重用。
STL即标准模板库,是c++中的标准库。正如名字所述,该库几乎所有的代码都采用模板技术。STL的主要目的是提供一些通用的代码,包括:容器、迭代器和算法3种。通过使用STL,一些原来需要很多行代码实现的技术在使用了STL后能够大大简化实现,从而提高开发效率。
通常在开发程序的时候,经常会遇到某些功能在代码的实现上都一样,不同的只是处理的对象类型不一样。这种情况下,最好对代码进行重构,将其中的共性提取出来,以实现代码的重用。c++模板技术是实现重用的一种方式。
面试题130 什么是函数模板***
分析:模板技术的使用,包括有函数模板和类模板这两种。通过使用函数模板,能够将函数参数化,使得程序能够使用不同的参数类型调用相同的代码。
函数模板技术定义了参数化的非成员函数,这使得程序能够使用不同的参数类型调用相同的函数,而至于使用何种类型,则是由编译器确定并从模板中生成相应类型的代码。编译器确定了模板函数的实际类型参数,称之为模板的实例化。下面代码是一个模板函数的定义:
template<class T> //定义模板标识
T Add(T a, T b) //函数原型
{
T result=a+b;
return a+b; //将两个参数使用“+”运算符进行运算,这两个参数并不知道是何种类型
}
如上代码所示,定义了一个名为Add的函数模板。该函数与一般函数的不同之处在于并没有明确的指出使用何种数据类型和返回值又是哪种数据类型。数据类型的确定是由使用者在使用时确定。
下面代码演示了如何定义一个函数模板,并且如何在程序中调用该函数。
#include<iostream> //包含标准输入输出头文件
#include <string> //包含c++的字符串处理头文件
using namespace std;
template<class T> //声明模板的标识
T Add(T a, T b) //函数原型
{
T result; //使用参数化的类型定义变量
result=a+b; //并使用参数的“+”运算符进行运算
return result; //返回运算结果
}
int main(int argc,char *argv[])
{
cout<<"2+3="<<Add(2,3)<<endl; //输出整型类的“+”运算结果
cout<<"sdf+123="<<Add(string("sdf"),string("123"))<<endl; //输出string类型的“+”运算结果
return 0;
}
在main函数中,函数模板共调用了两次。一次使用了整型参数,另一次使用了string类型参数,其运行结果输入如下:
2+3=5
sdf+123=sdf123
注意:要使得上述代码成功,那么参数化类型T应当能够使用运算符“+”进行运算,如代码所示,2和3是整型,对运算符“+”来说执行的是加法;而sdf和123是string类型,该类型使用“+”运算符时,其结果是将左右两个字符串相连成为新的字符串。
通过模板技术,函数模板的功能相当地强大,能够对不同的数据类型调用同一个函数进行处理,但是在使用函数模板时也会遇到问题,例如在代码中所定义的函数模板,需要类型T能够对“+”运算符进行运算,也就是说如果没有默认的“+”运算符实现时,要使用该函数模板,参数化的类型的实例类型就需要对运算符“+”进行重载,否则会遇到意想不到的结果,如下:
#include<iostream> //包含标准输入输出头文件
#include <string> //包含c++的字符串处理头文件
using namespace std;
template<class T> //声明模板的标识
T Min(T a, T b) //函数原型
{
if(a<b) //返回参数a和b中最小值
{return a;}
else
{return b;}
}
int main()
{
char *a="a"; //声明变量a,b,c,d
char *b="b";
char *c="c";
char *d="d";
cout<<"min(a,b):"<<Min(a,b)<<endl; 返回a和b中的最小值
cout<<"min(c,d):"<<Min(c,d)<<endl; 返回c和d中的最小值
return 0;
}
【答案】函数模板技术是指使用了模板技术定义了参数化类型的非成员函数,这使得程序能够使用不同的参数类型调用相同的函数。
面试题131 什么是类模板***
分析:类模板描述了能够管理其他数据类型的通用数据类型。类模板技术通常用于建立包含其他类型的容器类,例如,队列、链表和堆栈等。对于这些容器类来说,无论哪种数据类型,其操作方式是相同的,但是针对具体的数据类型却又是专用的。标准模板库STL就大量使用了这种技术。
类模板也是模板,因此在定义时需要以template开始,后面是目标形参表。除了模板标识符以外,其他的类定义和普通的类一样,如下面代码定义了一个类模板:
template<class T> //模板说明
class TemplateSample //定义类
{
private:
T& entity;
public:
void F(T& arg); //使用参数类型定义成员函数
}
该示例定义了一个类模板,类模板中的模板形参T需要用户在使用时进行定义,类模板定义的第一部分是模板说明,用于声明该类为类模板且声明参数化的数据类型。
template<class T> //模板说明
然后是类型的定义,和普通的类定义一样,可以使用参数类型定义类模板的成员变量,成员函数等。
在使用类模板时与函数模板有点不一样,调用时需要明确的指出使用何种数据类型,而不能由编译器自行制定,如下代码所示使用类模板定义一个变量:
TemplateSample<int> demo; //指出针对该模板使用int类型
注意:在实际使用该类模板之前,编译器不会为该模板生成任何代码。而是在又被调用时再由编译器为不同的数据类型根据类模板生成不同的代码。
在调用类模板中的成员函数时,和普通的类中的成员函数一样,如下代码所示调用类模板TemplateSample中的F(T&)函数:
demo.F(123); //调用类模板中的成员函数
类模板的模板参数定义除了class进行定义以外,还可以使用其他数据类型,但是至少需要有一个模板形参是使用class定义的,如下所示模板参数列表定义了3个参数:
template<class T1,class T2,int num> //定义多个模板参数,且其中一个直接使用int类型
在该示例中,前两个参数可以是任何类型的那个,但是第3个参数一定是int类型。在使用非类类型的模板形参时,调用的时候该形参传递的是一个值,而不是一种类型,例如下面的代码:
template<int,char,12> demo1; //使用非类类型的模板
#include<iostream>
template<class T,class T2,int num> //定义了类模板,其中一个参数是int类型
class CSampleTemplate
{
private:
T t1;
T2 t2;
public:
CSampeTemplate(T arg1,T2 arg2) //构造函数中使用模板参数
{
t1=arg1+num; //构造函数的参数的值分别于num相加
t2=arg2+num;
}
void Write() //输出成员变量的值
{std::cout<<"t1:"<<t1<<"t2:"<<t2<<endl;}
CSampleTemplate(void){}
};
int main()
{
CSampleTemplate<int,int,3> temp(1,2);
temp.Write();
return 0;
}
上述代码的输出结果如下:
t1:4 t2:5
模板技术是使用通用的方式管理其他数据类型,这些数据类型有自己的行为。而类模板主要是用于建立容器类,该容器类为其他的数据类型提供了通用的行为。该行为与具体的数据类型细节不相关的。例如类型Date有其自己的行为,字符串也是一样。但是具体到将这些数据类型存放于链表、堆栈和队列等容器中时,容器提供的行为是通用的,与具体的类型不相关。这时可以使用类模板技术来管理不同的数据类型。
如同函数模板一样,类模板同样可以进行定制。定制的内容包括完整的类模板的定制和类模板中成员函数的定制。定制的类模板和函数允许定义与特定类型相关的专门的行为。在实例化类模板时,定制的类或函数重定义类模板或模板的成员函数。
【答案】类模板是使用模板技术的类,描述了能够管理其他数据类型的通用的数据类型。类模板技术通常是用于建立包含其他类型的容器类,例如队列、链表和堆栈等。